xref: /qemu/hw/block/pflash_cfi02.c (revision 7f7bdcaf)
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 
3880c71a24SPeter Maydell #include "qemu/osdep.h"
3949ab747fSPaolo Bonzini #include "hw/hw.h"
4006f15217SMarkus Armbruster #include "hw/block/block.h"
4149ab747fSPaolo Bonzini #include "hw/block/flash.h"
42da34e65cSMarkus Armbruster #include "qapi/error.h"
4349ab747fSPaolo Bonzini #include "qemu/timer.h"
444be74634SMarkus Armbruster #include "sysemu/block-backend.h"
4549ab747fSPaolo Bonzini #include "qemu/host-utils.h"
460b8fa32fSMarkus Armbruster #include "qemu/module.h"
4749ab747fSPaolo Bonzini #include "hw/sysbus.h"
4813019f1fSPhilippe Mathieu-Daudé #include "trace.h"
4949ab747fSPaolo Bonzini 
506536987fSPhilippe Mathieu-Daudé #define PFLASH_DEBUG false
5149ab747fSPaolo Bonzini #define DPRINTF(fmt, ...)                                  \
5249ab747fSPaolo Bonzini do {                                                       \
536536987fSPhilippe Mathieu-Daudé     if (PFLASH_DEBUG) {                                    \
5456f99ea1SAntony Pavlov         fprintf(stderr, "PFLASH: " fmt, ## __VA_ARGS__);   \
556536987fSPhilippe Mathieu-Daudé     }                                                      \
5649ab747fSPaolo Bonzini } while (0)
5749ab747fSPaolo Bonzini 
5849ab747fSPaolo Bonzini #define PFLASH_LAZY_ROMD_THRESHOLD 42
5949ab747fSPaolo Bonzini 
60aeaf6c20SPhilippe Mathieu-Daudé /* Special write cycles for CFI queries. */
61aeaf6c20SPhilippe Mathieu-Daudé enum {
62aeaf6c20SPhilippe Mathieu-Daudé     WCYCLE_CFI              = 7,
63aeaf6c20SPhilippe Mathieu-Daudé };
64aeaf6c20SPhilippe Mathieu-Daudé 
6516434065SMarkus Armbruster struct PFlashCFI02 {
663509c396SHu Tao     /*< private >*/
673509c396SHu Tao     SysBusDevice parent_obj;
683509c396SHu Tao     /*< public >*/
693509c396SHu Tao 
704be74634SMarkus Armbruster     BlockBackend *blk;
7149ab747fSPaolo Bonzini     uint32_t sector_len;
7249ab747fSPaolo Bonzini     uint32_t nb_blocs;
7349ab747fSPaolo Bonzini     uint32_t chip_len;
7449ab747fSPaolo Bonzini     uint8_t mappings;
7549ab747fSPaolo Bonzini     uint8_t width;
7649ab747fSPaolo Bonzini     uint8_t be;
7749ab747fSPaolo Bonzini     int wcycle; /* if 0, the flash is read normally */
7849ab747fSPaolo Bonzini     int bypass;
7949ab747fSPaolo Bonzini     int ro;
8049ab747fSPaolo Bonzini     uint8_t cmd;
8149ab747fSPaolo Bonzini     uint8_t status;
8249ab747fSPaolo Bonzini     /* FIXME: implement array device properties */
8349ab747fSPaolo Bonzini     uint16_t ident0;
8449ab747fSPaolo Bonzini     uint16_t ident1;
8549ab747fSPaolo Bonzini     uint16_t ident2;
8649ab747fSPaolo Bonzini     uint16_t ident3;
8749ab747fSPaolo Bonzini     uint16_t unlock_addr0;
8849ab747fSPaolo Bonzini     uint16_t unlock_addr1;
8949ab747fSPaolo Bonzini     uint8_t cfi_table[0x52];
90d80cf1ebSStephen Checkoway     QEMUTimer timer;
9149ab747fSPaolo Bonzini     /* The device replicates the flash memory across its memory space.  Emulate
9249ab747fSPaolo Bonzini      * that by having a container (.mem) filled with an array of aliases
9349ab747fSPaolo Bonzini      * (.mem_mappings) pointing to the flash memory (.orig_mem).
9449ab747fSPaolo Bonzini      */
9549ab747fSPaolo Bonzini     MemoryRegion mem;
9649ab747fSPaolo Bonzini     MemoryRegion *mem_mappings;    /* array; one per mapping */
9749ab747fSPaolo Bonzini     MemoryRegion orig_mem;
9849ab747fSPaolo Bonzini     int rom_mode;
9949ab747fSPaolo Bonzini     int read_counter; /* used for lazy switch-back to rom mode */
10049ab747fSPaolo Bonzini     char *name;
10149ab747fSPaolo Bonzini     void *storage;
10249ab747fSPaolo Bonzini };
10349ab747fSPaolo Bonzini 
10449ab747fSPaolo Bonzini /*
1051d311e73SPhilippe Mathieu-Daudé  * Toggle status bit DQ7.
1061d311e73SPhilippe Mathieu-Daudé  */
1071d311e73SPhilippe Mathieu-Daudé static inline void toggle_dq7(PFlashCFI02 *pfl)
1081d311e73SPhilippe Mathieu-Daudé {
1091d311e73SPhilippe Mathieu-Daudé     pfl->status ^= 0x80;
1101d311e73SPhilippe Mathieu-Daudé }
1111d311e73SPhilippe Mathieu-Daudé 
1121d311e73SPhilippe Mathieu-Daudé /*
1131d311e73SPhilippe Mathieu-Daudé  * Set status bit DQ7 to bit 7 of value.
1141d311e73SPhilippe Mathieu-Daudé  */
1151d311e73SPhilippe Mathieu-Daudé static inline void set_dq7(PFlashCFI02 *pfl, uint8_t value)
1161d311e73SPhilippe Mathieu-Daudé {
1171d311e73SPhilippe Mathieu-Daudé     pfl->status &= 0x7F;
1181d311e73SPhilippe Mathieu-Daudé     pfl->status |= value & 0x80;
1191d311e73SPhilippe Mathieu-Daudé }
1201d311e73SPhilippe Mathieu-Daudé 
1211d311e73SPhilippe Mathieu-Daudé /*
1221d311e73SPhilippe Mathieu-Daudé  * Toggle status bit DQ6.
1231d311e73SPhilippe Mathieu-Daudé  */
1241d311e73SPhilippe Mathieu-Daudé static inline void toggle_dq6(PFlashCFI02 *pfl)
1251d311e73SPhilippe Mathieu-Daudé {
1261d311e73SPhilippe Mathieu-Daudé     pfl->status ^= 0x40;
1271d311e73SPhilippe Mathieu-Daudé }
1281d311e73SPhilippe Mathieu-Daudé 
1291d311e73SPhilippe Mathieu-Daudé /*
13049ab747fSPaolo Bonzini  * Set up replicated mappings of the same region.
13149ab747fSPaolo Bonzini  */
13216434065SMarkus Armbruster static void pflash_setup_mappings(PFlashCFI02 *pfl)
13349ab747fSPaolo Bonzini {
13449ab747fSPaolo Bonzini     unsigned i;
13549ab747fSPaolo Bonzini     hwaddr size = memory_region_size(&pfl->orig_mem);
13649ab747fSPaolo Bonzini 
1372d256e6fSPaolo Bonzini     memory_region_init(&pfl->mem, OBJECT(pfl), "pflash", pfl->mappings * size);
13849ab747fSPaolo Bonzini     pfl->mem_mappings = g_new(MemoryRegion, pfl->mappings);
13949ab747fSPaolo Bonzini     for (i = 0; i < pfl->mappings; ++i) {
1402d256e6fSPaolo Bonzini         memory_region_init_alias(&pfl->mem_mappings[i], OBJECT(pfl),
1412d256e6fSPaolo Bonzini                                  "pflash-alias", &pfl->orig_mem, 0, size);
14249ab747fSPaolo Bonzini         memory_region_add_subregion(&pfl->mem, i * size, &pfl->mem_mappings[i]);
14349ab747fSPaolo Bonzini     }
14449ab747fSPaolo Bonzini }
14549ab747fSPaolo Bonzini 
14616434065SMarkus Armbruster static void pflash_register_memory(PFlashCFI02 *pfl, int rom_mode)
14749ab747fSPaolo Bonzini {
1485f9a5ea1SJan Kiszka     memory_region_rom_device_set_romd(&pfl->orig_mem, rom_mode);
14949ab747fSPaolo Bonzini     pfl->rom_mode = rom_mode;
15049ab747fSPaolo Bonzini }
15149ab747fSPaolo Bonzini 
15249ab747fSPaolo Bonzini static void pflash_timer (void *opaque)
15349ab747fSPaolo Bonzini {
15416434065SMarkus Armbruster     PFlashCFI02 *pfl = opaque;
15549ab747fSPaolo Bonzini 
15613019f1fSPhilippe Mathieu-Daudé     trace_pflash_timer_expired(pfl->cmd);
15749ab747fSPaolo Bonzini     /* Reset flash */
1581d311e73SPhilippe Mathieu-Daudé     toggle_dq7(pfl);
15949ab747fSPaolo Bonzini     if (pfl->bypass) {
16049ab747fSPaolo Bonzini         pfl->wcycle = 2;
16149ab747fSPaolo Bonzini     } else {
16249ab747fSPaolo Bonzini         pflash_register_memory(pfl, 1);
16349ab747fSPaolo Bonzini         pfl->wcycle = 0;
16449ab747fSPaolo Bonzini     }
16549ab747fSPaolo Bonzini     pfl->cmd = 0;
16649ab747fSPaolo Bonzini }
16749ab747fSPaolo Bonzini 
16816434065SMarkus Armbruster static uint32_t pflash_read(PFlashCFI02 *pfl, hwaddr offset,
16949ab747fSPaolo Bonzini                             int width, int be)
17049ab747fSPaolo Bonzini {
17149ab747fSPaolo Bonzini     hwaddr boff;
17249ab747fSPaolo Bonzini     uint32_t ret;
17349ab747fSPaolo Bonzini     uint8_t *p;
17449ab747fSPaolo Bonzini 
17549ab747fSPaolo Bonzini     ret = -1;
17649ab747fSPaolo Bonzini     /* Lazy reset to ROMD mode after a certain amount of read accesses */
17749ab747fSPaolo Bonzini     if (!pfl->rom_mode && pfl->wcycle == 0 &&
17849ab747fSPaolo Bonzini         ++pfl->read_counter > PFLASH_LAZY_ROMD_THRESHOLD) {
17949ab747fSPaolo Bonzini         pflash_register_memory(pfl, 1);
18049ab747fSPaolo Bonzini     }
18149ab747fSPaolo Bonzini     offset &= pfl->chip_len - 1;
18249ab747fSPaolo Bonzini     boff = offset & 0xFF;
18349ab747fSPaolo Bonzini     if (pfl->width == 2)
18449ab747fSPaolo Bonzini         boff = boff >> 1;
18549ab747fSPaolo Bonzini     else if (pfl->width == 4)
18649ab747fSPaolo Bonzini         boff = boff >> 2;
18749ab747fSPaolo Bonzini     switch (pfl->cmd) {
18849ab747fSPaolo Bonzini     default:
18949ab747fSPaolo Bonzini         /* This should never happen : reset state & treat it as a read*/
19049ab747fSPaolo Bonzini         DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd);
19149ab747fSPaolo Bonzini         pfl->wcycle = 0;
19249ab747fSPaolo Bonzini         pfl->cmd = 0;
19349ab747fSPaolo Bonzini         /* fall through to the read code */
19449ab747fSPaolo Bonzini     case 0x80:
19549ab747fSPaolo Bonzini         /* We accept reads during second unlock sequence... */
19649ab747fSPaolo Bonzini     case 0x00:
19749ab747fSPaolo Bonzini     flash_read:
19849ab747fSPaolo Bonzini         /* Flash area read */
19949ab747fSPaolo Bonzini         p = pfl->storage;
20049ab747fSPaolo Bonzini         switch (width) {
20149ab747fSPaolo Bonzini         case 1:
20249ab747fSPaolo Bonzini             ret = p[offset];
20349ab747fSPaolo Bonzini             break;
20449ab747fSPaolo Bonzini         case 2:
20549ab747fSPaolo Bonzini             if (be) {
20649ab747fSPaolo Bonzini                 ret = p[offset] << 8;
20749ab747fSPaolo Bonzini                 ret |= p[offset + 1];
20849ab747fSPaolo Bonzini             } else {
20949ab747fSPaolo Bonzini                 ret = p[offset];
21049ab747fSPaolo Bonzini                 ret |= p[offset + 1] << 8;
21149ab747fSPaolo Bonzini             }
21249ab747fSPaolo Bonzini             break;
21349ab747fSPaolo Bonzini         case 4:
21449ab747fSPaolo Bonzini             if (be) {
21549ab747fSPaolo Bonzini                 ret = p[offset] << 24;
21649ab747fSPaolo Bonzini                 ret |= p[offset + 1] << 16;
21749ab747fSPaolo Bonzini                 ret |= p[offset + 2] << 8;
21849ab747fSPaolo Bonzini                 ret |= p[offset + 3];
21949ab747fSPaolo Bonzini             } else {
22049ab747fSPaolo Bonzini                 ret = p[offset];
22149ab747fSPaolo Bonzini                 ret |= p[offset + 1] << 8;
22249ab747fSPaolo Bonzini                 ret |= p[offset + 2] << 16;
22349ab747fSPaolo Bonzini                 ret |= p[offset + 3] << 24;
22449ab747fSPaolo Bonzini             }
22549ab747fSPaolo Bonzini             break;
22649ab747fSPaolo Bonzini         }
227c1474acdSPhilippe Mathieu-Daudé         trace_pflash_data_read(offset, width << 1, ret);
22849ab747fSPaolo Bonzini         break;
22949ab747fSPaolo Bonzini     case 0x90:
23049ab747fSPaolo Bonzini         /* flash ID read */
23149ab747fSPaolo Bonzini         switch (boff) {
23249ab747fSPaolo Bonzini         case 0x00:
23349ab747fSPaolo Bonzini         case 0x01:
23449ab747fSPaolo Bonzini             ret = boff & 0x01 ? pfl->ident1 : pfl->ident0;
23549ab747fSPaolo Bonzini             break;
23649ab747fSPaolo Bonzini         case 0x02:
23749ab747fSPaolo Bonzini             ret = 0x00; /* Pretend all sectors are unprotected */
23849ab747fSPaolo Bonzini             break;
23949ab747fSPaolo Bonzini         case 0x0E:
24049ab747fSPaolo Bonzini         case 0x0F:
24149ab747fSPaolo Bonzini             ret = boff & 0x01 ? pfl->ident3 : pfl->ident2;
242*7f7bdcafSPhilippe Mathieu-Daudé             if (ret != (uint8_t)-1) {
24349ab747fSPaolo Bonzini                 break;
244*7f7bdcafSPhilippe Mathieu-Daudé             }
245*7f7bdcafSPhilippe Mathieu-Daudé             /* Fall through to data read. */
24649ab747fSPaolo Bonzini         default:
24749ab747fSPaolo Bonzini             goto flash_read;
24849ab747fSPaolo Bonzini         }
2496536987fSPhilippe Mathieu-Daudé         DPRINTF("%s: ID " TARGET_FMT_plx " %" PRIx32 "\n", __func__, boff, ret);
25049ab747fSPaolo Bonzini         break;
25149ab747fSPaolo Bonzini     case 0xA0:
25249ab747fSPaolo Bonzini     case 0x10:
25349ab747fSPaolo Bonzini     case 0x30:
25449ab747fSPaolo Bonzini         /* Status register read */
25549ab747fSPaolo Bonzini         ret = pfl->status;
2566536987fSPhilippe Mathieu-Daudé         DPRINTF("%s: status %" PRIx32 "\n", __func__, ret);
2571d311e73SPhilippe Mathieu-Daudé         toggle_dq6(pfl);
25849ab747fSPaolo Bonzini         break;
25949ab747fSPaolo Bonzini     case 0x98:
26049ab747fSPaolo Bonzini         /* CFI query mode */
26107c13a71SPhilippe Mathieu-Daudé         if (boff < sizeof(pfl->cfi_table)) {
26249ab747fSPaolo Bonzini             ret = pfl->cfi_table[boff];
26307c13a71SPhilippe Mathieu-Daudé         } else {
26407c13a71SPhilippe Mathieu-Daudé             ret = 0;
26507c13a71SPhilippe Mathieu-Daudé         }
26649ab747fSPaolo Bonzini         break;
26749ab747fSPaolo Bonzini     }
268e8aa2d95SPhilippe Mathieu-Daudé     trace_pflash_io_read(offset, width, width << 1, ret, pfl->cmd, pfl->wcycle);
26949ab747fSPaolo Bonzini 
27049ab747fSPaolo Bonzini     return ret;
27149ab747fSPaolo Bonzini }
27249ab747fSPaolo Bonzini 
27349ab747fSPaolo Bonzini /* update flash content on disk */
27416434065SMarkus Armbruster static void pflash_update(PFlashCFI02 *pfl, int offset,
27549ab747fSPaolo Bonzini                           int size)
27649ab747fSPaolo Bonzini {
27749ab747fSPaolo Bonzini     int offset_end;
2784be74634SMarkus Armbruster     if (pfl->blk) {
27949ab747fSPaolo Bonzini         offset_end = offset + size;
280098e732dSEric Blake         /* widen to sector boundaries */
281098e732dSEric Blake         offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE);
282098e732dSEric Blake         offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE);
283098e732dSEric Blake         blk_pwrite(pfl->blk, offset, pfl->storage + offset,
284098e732dSEric Blake                    offset_end - offset, 0);
28549ab747fSPaolo Bonzini     }
28649ab747fSPaolo Bonzini }
28749ab747fSPaolo Bonzini 
28816434065SMarkus Armbruster static void pflash_write(PFlashCFI02 *pfl, hwaddr offset,
28949ab747fSPaolo Bonzini                          uint32_t value, int width, int be)
29049ab747fSPaolo Bonzini {
29149ab747fSPaolo Bonzini     hwaddr boff;
29249ab747fSPaolo Bonzini     uint8_t *p;
29349ab747fSPaolo Bonzini     uint8_t cmd;
29449ab747fSPaolo Bonzini 
295e8aa2d95SPhilippe Mathieu-Daudé     trace_pflash_io_write(offset, width, width << 1, value, pfl->wcycle);
29649ab747fSPaolo Bonzini     cmd = value;
29749ab747fSPaolo Bonzini     if (pfl->cmd != 0xA0 && cmd == 0xF0) {
29849ab747fSPaolo Bonzini         goto reset_flash;
29949ab747fSPaolo Bonzini     }
30049ab747fSPaolo Bonzini     offset &= pfl->chip_len - 1;
30149ab747fSPaolo Bonzini 
30249ab747fSPaolo Bonzini     boff = offset & (pfl->sector_len - 1);
30349ab747fSPaolo Bonzini     if (pfl->width == 2)
30449ab747fSPaolo Bonzini         boff = boff >> 1;
30549ab747fSPaolo Bonzini     else if (pfl->width == 4)
30649ab747fSPaolo Bonzini         boff = boff >> 2;
30749ab747fSPaolo Bonzini     switch (pfl->wcycle) {
30849ab747fSPaolo Bonzini     case 0:
30949ab747fSPaolo Bonzini         /* Set the device in I/O access mode if required */
31049ab747fSPaolo Bonzini         if (pfl->rom_mode)
31149ab747fSPaolo Bonzini             pflash_register_memory(pfl, 0);
31249ab747fSPaolo Bonzini         pfl->read_counter = 0;
31349ab747fSPaolo Bonzini         /* We're in read mode */
31449ab747fSPaolo Bonzini     check_unlock0:
31549ab747fSPaolo Bonzini         if (boff == 0x55 && cmd == 0x98) {
31649ab747fSPaolo Bonzini         enter_CFI_mode:
31749ab747fSPaolo Bonzini             /* Enter CFI query mode */
318aeaf6c20SPhilippe Mathieu-Daudé             pfl->wcycle = WCYCLE_CFI;
31949ab747fSPaolo Bonzini             pfl->cmd = 0x98;
32049ab747fSPaolo Bonzini             return;
32149ab747fSPaolo Bonzini         }
32249ab747fSPaolo Bonzini         if (boff != pfl->unlock_addr0 || cmd != 0xAA) {
32349ab747fSPaolo Bonzini             DPRINTF("%s: unlock0 failed " TARGET_FMT_plx " %02x %04x\n",
32449ab747fSPaolo Bonzini                     __func__, boff, cmd, pfl->unlock_addr0);
32549ab747fSPaolo Bonzini             goto reset_flash;
32649ab747fSPaolo Bonzini         }
32749ab747fSPaolo Bonzini         DPRINTF("%s: unlock sequence started\n", __func__);
32849ab747fSPaolo Bonzini         break;
32949ab747fSPaolo Bonzini     case 1:
33049ab747fSPaolo Bonzini         /* We started an unlock sequence */
33149ab747fSPaolo Bonzini     check_unlock1:
33249ab747fSPaolo Bonzini         if (boff != pfl->unlock_addr1 || cmd != 0x55) {
33349ab747fSPaolo Bonzini             DPRINTF("%s: unlock1 failed " TARGET_FMT_plx " %02x\n", __func__,
33449ab747fSPaolo Bonzini                     boff, cmd);
33549ab747fSPaolo Bonzini             goto reset_flash;
33649ab747fSPaolo Bonzini         }
33749ab747fSPaolo Bonzini         DPRINTF("%s: unlock sequence done\n", __func__);
33849ab747fSPaolo Bonzini         break;
33949ab747fSPaolo Bonzini     case 2:
34049ab747fSPaolo Bonzini         /* We finished an unlock sequence */
34149ab747fSPaolo Bonzini         if (!pfl->bypass && boff != pfl->unlock_addr0) {
34249ab747fSPaolo Bonzini             DPRINTF("%s: command failed " TARGET_FMT_plx " %02x\n", __func__,
34349ab747fSPaolo Bonzini                     boff, cmd);
34449ab747fSPaolo Bonzini             goto reset_flash;
34549ab747fSPaolo Bonzini         }
34649ab747fSPaolo Bonzini         switch (cmd) {
34749ab747fSPaolo Bonzini         case 0x20:
34849ab747fSPaolo Bonzini             pfl->bypass = 1;
34949ab747fSPaolo Bonzini             goto do_bypass;
35049ab747fSPaolo Bonzini         case 0x80:
35149ab747fSPaolo Bonzini         case 0x90:
35249ab747fSPaolo Bonzini         case 0xA0:
35349ab747fSPaolo Bonzini             pfl->cmd = cmd;
35449ab747fSPaolo Bonzini             DPRINTF("%s: starting command %02x\n", __func__, cmd);
35549ab747fSPaolo Bonzini             break;
35649ab747fSPaolo Bonzini         default:
35749ab747fSPaolo Bonzini             DPRINTF("%s: unknown command %02x\n", __func__, cmd);
35849ab747fSPaolo Bonzini             goto reset_flash;
35949ab747fSPaolo Bonzini         }
36049ab747fSPaolo Bonzini         break;
36149ab747fSPaolo Bonzini     case 3:
36249ab747fSPaolo Bonzini         switch (pfl->cmd) {
36349ab747fSPaolo Bonzini         case 0x80:
36449ab747fSPaolo Bonzini             /* We need another unlock sequence */
36549ab747fSPaolo Bonzini             goto check_unlock0;
36649ab747fSPaolo Bonzini         case 0xA0:
367c1474acdSPhilippe Mathieu-Daudé             trace_pflash_data_write(offset, width << 1, value, 0);
36849ab747fSPaolo Bonzini             p = pfl->storage;
36949ab747fSPaolo Bonzini             if (!pfl->ro) {
37049ab747fSPaolo Bonzini                 switch (width) {
37149ab747fSPaolo Bonzini                 case 1:
37249ab747fSPaolo Bonzini                     p[offset] &= value;
37349ab747fSPaolo Bonzini                     pflash_update(pfl, offset, 1);
37449ab747fSPaolo Bonzini                     break;
37549ab747fSPaolo Bonzini                 case 2:
37649ab747fSPaolo Bonzini                     if (be) {
37749ab747fSPaolo Bonzini                         p[offset] &= value >> 8;
37849ab747fSPaolo Bonzini                         p[offset + 1] &= value;
37949ab747fSPaolo Bonzini                     } else {
38049ab747fSPaolo Bonzini                         p[offset] &= value;
38149ab747fSPaolo Bonzini                         p[offset + 1] &= value >> 8;
38249ab747fSPaolo Bonzini                     }
38349ab747fSPaolo Bonzini                     pflash_update(pfl, offset, 2);
38449ab747fSPaolo Bonzini                     break;
38549ab747fSPaolo Bonzini                 case 4:
38649ab747fSPaolo Bonzini                     if (be) {
38749ab747fSPaolo Bonzini                         p[offset] &= value >> 24;
38849ab747fSPaolo Bonzini                         p[offset + 1] &= value >> 16;
38949ab747fSPaolo Bonzini                         p[offset + 2] &= value >> 8;
39049ab747fSPaolo Bonzini                         p[offset + 3] &= value;
39149ab747fSPaolo Bonzini                     } else {
39249ab747fSPaolo Bonzini                         p[offset] &= value;
39349ab747fSPaolo Bonzini                         p[offset + 1] &= value >> 8;
39449ab747fSPaolo Bonzini                         p[offset + 2] &= value >> 16;
39549ab747fSPaolo Bonzini                         p[offset + 3] &= value >> 24;
39649ab747fSPaolo Bonzini                     }
39749ab747fSPaolo Bonzini                     pflash_update(pfl, offset, 4);
39849ab747fSPaolo Bonzini                     break;
39949ab747fSPaolo Bonzini                 }
40049ab747fSPaolo Bonzini             }
4011d311e73SPhilippe Mathieu-Daudé             /*
4021d311e73SPhilippe Mathieu-Daudé              * While programming, status bit DQ7 should hold the opposite
4031d311e73SPhilippe Mathieu-Daudé              * value from how it was programmed.
4041d311e73SPhilippe Mathieu-Daudé              */
4051d311e73SPhilippe Mathieu-Daudé             set_dq7(pfl, ~value);
40649ab747fSPaolo Bonzini             /* Let's pretend write is immediate */
40749ab747fSPaolo Bonzini             if (pfl->bypass)
40849ab747fSPaolo Bonzini                 goto do_bypass;
40949ab747fSPaolo Bonzini             goto reset_flash;
41049ab747fSPaolo Bonzini         case 0x90:
41149ab747fSPaolo Bonzini             if (pfl->bypass && cmd == 0x00) {
41249ab747fSPaolo Bonzini                 /* Unlock bypass reset */
41349ab747fSPaolo Bonzini                 goto reset_flash;
41449ab747fSPaolo Bonzini             }
41549ab747fSPaolo Bonzini             /* We can enter CFI query mode from autoselect mode */
41649ab747fSPaolo Bonzini             if (boff == 0x55 && cmd == 0x98)
41749ab747fSPaolo Bonzini                 goto enter_CFI_mode;
41849ab747fSPaolo Bonzini             /* No break here */
41949ab747fSPaolo Bonzini         default:
42049ab747fSPaolo Bonzini             DPRINTF("%s: invalid write for command %02x\n",
42149ab747fSPaolo Bonzini                     __func__, pfl->cmd);
42249ab747fSPaolo Bonzini             goto reset_flash;
42349ab747fSPaolo Bonzini         }
42449ab747fSPaolo Bonzini     case 4:
42549ab747fSPaolo Bonzini         switch (pfl->cmd) {
42649ab747fSPaolo Bonzini         case 0xA0:
42749ab747fSPaolo Bonzini             /* Ignore writes while flash data write is occurring */
42849ab747fSPaolo Bonzini             /* As we suppose write is immediate, this should never happen */
42949ab747fSPaolo Bonzini             return;
43049ab747fSPaolo Bonzini         case 0x80:
43149ab747fSPaolo Bonzini             goto check_unlock1;
43249ab747fSPaolo Bonzini         default:
43349ab747fSPaolo Bonzini             /* Should never happen */
43449ab747fSPaolo Bonzini             DPRINTF("%s: invalid command state %02x (wc 4)\n",
43549ab747fSPaolo Bonzini                     __func__, pfl->cmd);
43649ab747fSPaolo Bonzini             goto reset_flash;
43749ab747fSPaolo Bonzini         }
43849ab747fSPaolo Bonzini         break;
43949ab747fSPaolo Bonzini     case 5:
44049ab747fSPaolo Bonzini         switch (cmd) {
44149ab747fSPaolo Bonzini         case 0x10:
44249ab747fSPaolo Bonzini             if (boff != pfl->unlock_addr0) {
44349ab747fSPaolo Bonzini                 DPRINTF("%s: chip erase: invalid address " TARGET_FMT_plx "\n",
44449ab747fSPaolo Bonzini                         __func__, offset);
44549ab747fSPaolo Bonzini                 goto reset_flash;
44649ab747fSPaolo Bonzini             }
44749ab747fSPaolo Bonzini             /* Chip erase */
44849ab747fSPaolo Bonzini             DPRINTF("%s: start chip erase\n", __func__);
44949ab747fSPaolo Bonzini             if (!pfl->ro) {
45049ab747fSPaolo Bonzini                 memset(pfl->storage, 0xFF, pfl->chip_len);
45149ab747fSPaolo Bonzini                 pflash_update(pfl, 0, pfl->chip_len);
45249ab747fSPaolo Bonzini             }
4531d311e73SPhilippe Mathieu-Daudé             set_dq7(pfl, 0x00);
45449ab747fSPaolo Bonzini             /* Let's wait 5 seconds before chip erase is done */
455d80cf1ebSStephen Checkoway             timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
45673bcb24dSRutuja Shah                       (NANOSECONDS_PER_SECOND * 5));
45749ab747fSPaolo Bonzini             break;
45849ab747fSPaolo Bonzini         case 0x30:
45949ab747fSPaolo Bonzini             /* Sector erase */
46049ab747fSPaolo Bonzini             p = pfl->storage;
46149ab747fSPaolo Bonzini             offset &= ~(pfl->sector_len - 1);
46249ab747fSPaolo Bonzini             DPRINTF("%s: start sector erase at " TARGET_FMT_plx "\n", __func__,
46349ab747fSPaolo Bonzini                     offset);
46449ab747fSPaolo Bonzini             if (!pfl->ro) {
46549ab747fSPaolo Bonzini                 memset(p + offset, 0xFF, pfl->sector_len);
46649ab747fSPaolo Bonzini                 pflash_update(pfl, offset, pfl->sector_len);
46749ab747fSPaolo Bonzini             }
4681d311e73SPhilippe Mathieu-Daudé             set_dq7(pfl, 0x00);
46949ab747fSPaolo Bonzini             /* Let's wait 1/2 second before sector erase is done */
470d80cf1ebSStephen Checkoway             timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
47173bcb24dSRutuja Shah                       (NANOSECONDS_PER_SECOND / 2));
47249ab747fSPaolo Bonzini             break;
47349ab747fSPaolo Bonzini         default:
47449ab747fSPaolo Bonzini             DPRINTF("%s: invalid command %02x (wc 5)\n", __func__, cmd);
47549ab747fSPaolo Bonzini             goto reset_flash;
47649ab747fSPaolo Bonzini         }
47749ab747fSPaolo Bonzini         pfl->cmd = cmd;
47849ab747fSPaolo Bonzini         break;
47949ab747fSPaolo Bonzini     case 6:
48049ab747fSPaolo Bonzini         switch (pfl->cmd) {
48149ab747fSPaolo Bonzini         case 0x10:
48249ab747fSPaolo Bonzini             /* Ignore writes during chip erase */
48349ab747fSPaolo Bonzini             return;
48449ab747fSPaolo Bonzini         case 0x30:
48549ab747fSPaolo Bonzini             /* Ignore writes during sector erase */
48649ab747fSPaolo Bonzini             return;
48749ab747fSPaolo Bonzini         default:
48849ab747fSPaolo Bonzini             /* Should never happen */
48949ab747fSPaolo Bonzini             DPRINTF("%s: invalid command state %02x (wc 6)\n",
49049ab747fSPaolo Bonzini                     __func__, pfl->cmd);
49149ab747fSPaolo Bonzini             goto reset_flash;
49249ab747fSPaolo Bonzini         }
49349ab747fSPaolo Bonzini         break;
494aeaf6c20SPhilippe Mathieu-Daudé     /* Special values for CFI queries */
495aeaf6c20SPhilippe Mathieu-Daudé     case WCYCLE_CFI:
49649ab747fSPaolo Bonzini         DPRINTF("%s: invalid write in CFI query mode\n", __func__);
49749ab747fSPaolo Bonzini         goto reset_flash;
49849ab747fSPaolo Bonzini     default:
49949ab747fSPaolo Bonzini         /* Should never happen */
50049ab747fSPaolo Bonzini         DPRINTF("%s: invalid write state (wc 7)\n",  __func__);
50149ab747fSPaolo Bonzini         goto reset_flash;
50249ab747fSPaolo Bonzini     }
50349ab747fSPaolo Bonzini     pfl->wcycle++;
50449ab747fSPaolo Bonzini 
50549ab747fSPaolo Bonzini     return;
50649ab747fSPaolo Bonzini 
50749ab747fSPaolo Bonzini     /* Reset flash */
50849ab747fSPaolo Bonzini  reset_flash:
50913019f1fSPhilippe Mathieu-Daudé     trace_pflash_reset();
51049ab747fSPaolo Bonzini     pfl->bypass = 0;
51149ab747fSPaolo Bonzini     pfl->wcycle = 0;
51249ab747fSPaolo Bonzini     pfl->cmd = 0;
51349ab747fSPaolo Bonzini     return;
51449ab747fSPaolo Bonzini 
51549ab747fSPaolo Bonzini  do_bypass:
51649ab747fSPaolo Bonzini     pfl->wcycle = 2;
51749ab747fSPaolo Bonzini     pfl->cmd = 0;
51849ab747fSPaolo Bonzini }
51949ab747fSPaolo Bonzini 
520a4afb28dSPeter Maydell static uint64_t pflash_be_readfn(void *opaque, hwaddr addr, unsigned size)
52149ab747fSPaolo Bonzini {
522a4afb28dSPeter Maydell     return pflash_read(opaque, addr, size, 1);
52349ab747fSPaolo Bonzini }
52449ab747fSPaolo Bonzini 
525a4afb28dSPeter Maydell static void pflash_be_writefn(void *opaque, hwaddr addr,
526a4afb28dSPeter Maydell                               uint64_t value, unsigned size)
52749ab747fSPaolo Bonzini {
528a4afb28dSPeter Maydell     pflash_write(opaque, addr, value, size, 1);
52949ab747fSPaolo Bonzini }
53049ab747fSPaolo Bonzini 
531a4afb28dSPeter Maydell static uint64_t pflash_le_readfn(void *opaque, hwaddr addr, unsigned size)
53249ab747fSPaolo Bonzini {
533a4afb28dSPeter Maydell     return pflash_read(opaque, addr, size, 0);
53449ab747fSPaolo Bonzini }
53549ab747fSPaolo Bonzini 
536a4afb28dSPeter Maydell static void pflash_le_writefn(void *opaque, hwaddr addr,
537a4afb28dSPeter Maydell                               uint64_t value, unsigned size)
53849ab747fSPaolo Bonzini {
539a4afb28dSPeter Maydell     pflash_write(opaque, addr, value, size, 0);
54049ab747fSPaolo Bonzini }
54149ab747fSPaolo Bonzini 
54249ab747fSPaolo Bonzini static const MemoryRegionOps pflash_cfi02_ops_be = {
543a4afb28dSPeter Maydell     .read = pflash_be_readfn,
544a4afb28dSPeter Maydell     .write = pflash_be_writefn,
545a4afb28dSPeter Maydell     .valid.min_access_size = 1,
546a4afb28dSPeter Maydell     .valid.max_access_size = 4,
54749ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
54849ab747fSPaolo Bonzini };
54949ab747fSPaolo Bonzini 
55049ab747fSPaolo Bonzini static const MemoryRegionOps pflash_cfi02_ops_le = {
551a4afb28dSPeter Maydell     .read = pflash_le_readfn,
552a4afb28dSPeter Maydell     .write = pflash_le_writefn,
553a4afb28dSPeter Maydell     .valid.min_access_size = 1,
554a4afb28dSPeter Maydell     .valid.max_access_size = 4,
55549ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
55649ab747fSPaolo Bonzini };
55749ab747fSPaolo Bonzini 
558da3bd642SHu Tao static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
55949ab747fSPaolo Bonzini {
560e7b62741SMarkus Armbruster     PFlashCFI02 *pfl = PFLASH_CFI02(dev);
56149ab747fSPaolo Bonzini     uint32_t chip_len;
56249ab747fSPaolo Bonzini     int ret;
56333e0eb52SHu Tao     Error *local_err = NULL;
56449ab747fSPaolo Bonzini 
5658929fc3aSZiyue Yang     if (pfl->sector_len == 0) {
5668929fc3aSZiyue Yang         error_setg(errp, "attribute \"sector-length\" not specified or zero.");
5678929fc3aSZiyue Yang         return;
5688929fc3aSZiyue Yang     }
5698929fc3aSZiyue Yang     if (pfl->nb_blocs == 0) {
5708929fc3aSZiyue Yang         error_setg(errp, "attribute \"num-blocks\" not specified or zero.");
5718929fc3aSZiyue Yang         return;
5728929fc3aSZiyue Yang     }
5738929fc3aSZiyue Yang     if (pfl->name == NULL) {
5748929fc3aSZiyue Yang         error_setg(errp, "attribute \"name\" not specified.");
5758929fc3aSZiyue Yang         return;
5768929fc3aSZiyue Yang     }
5778929fc3aSZiyue Yang 
57849ab747fSPaolo Bonzini     chip_len = pfl->sector_len * pfl->nb_blocs;
57949ab747fSPaolo Bonzini 
580bba3ddf7SPeter Maydell     memory_region_init_rom_device(&pfl->orig_mem, OBJECT(pfl), pfl->be ?
58149ab747fSPaolo Bonzini                                   &pflash_cfi02_ops_be : &pflash_cfi02_ops_le,
58233e0eb52SHu Tao                                   pfl, pfl->name, chip_len, &local_err);
58333e0eb52SHu Tao     if (local_err) {
58433e0eb52SHu Tao         error_propagate(errp, local_err);
58533e0eb52SHu Tao         return;
58633e0eb52SHu Tao     }
58733e0eb52SHu Tao 
58849ab747fSPaolo Bonzini     pfl->storage = memory_region_get_ram_ptr(&pfl->orig_mem);
58949ab747fSPaolo Bonzini     pfl->chip_len = chip_len;
590a17c17a2SKevin Wolf 
591a17c17a2SKevin Wolf     if (pfl->blk) {
592a17c17a2SKevin Wolf         uint64_t perm;
593a17c17a2SKevin Wolf         pfl->ro = blk_is_read_only(pfl->blk);
594a17c17a2SKevin Wolf         perm = BLK_PERM_CONSISTENT_READ | (pfl->ro ? 0 : BLK_PERM_WRITE);
595a17c17a2SKevin Wolf         ret = blk_set_perm(pfl->blk, perm, BLK_PERM_ALL, errp);
596a17c17a2SKevin Wolf         if (ret < 0) {
597a17c17a2SKevin Wolf             return;
598a17c17a2SKevin Wolf         }
599a17c17a2SKevin Wolf     } else {
600a17c17a2SKevin Wolf         pfl->ro = 0;
601a17c17a2SKevin Wolf     }
602a17c17a2SKevin Wolf 
6034be74634SMarkus Armbruster     if (pfl->blk) {
60406f15217SMarkus Armbruster         if (!blk_check_size_and_read_all(pfl->blk, pfl->storage, chip_len,
60506f15217SMarkus Armbruster                                          errp)) {
606da3bd642SHu Tao             vmstate_unregister_ram(&pfl->orig_mem, DEVICE(pfl));
607da3bd642SHu Tao             return;
60849ab747fSPaolo Bonzini         }
60949ab747fSPaolo Bonzini     }
61049ab747fSPaolo Bonzini 
61149ab747fSPaolo Bonzini     pflash_setup_mappings(pfl);
61249ab747fSPaolo Bonzini     pfl->rom_mode = 1;
613da3bd642SHu Tao     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem);
61449ab747fSPaolo Bonzini 
615d80cf1ebSStephen Checkoway     timer_init_ns(&pfl->timer, QEMU_CLOCK_VIRTUAL, pflash_timer, pfl);
61649ab747fSPaolo Bonzini     pfl->wcycle = 0;
61749ab747fSPaolo Bonzini     pfl->cmd = 0;
61849ab747fSPaolo Bonzini     pfl->status = 0;
61949ab747fSPaolo Bonzini     /* Hardcoded CFI table (mostly from SG29 Spansion flash) */
62049ab747fSPaolo Bonzini     /* Standard "QRY" string */
62149ab747fSPaolo Bonzini     pfl->cfi_table[0x10] = 'Q';
62249ab747fSPaolo Bonzini     pfl->cfi_table[0x11] = 'R';
62349ab747fSPaolo Bonzini     pfl->cfi_table[0x12] = 'Y';
62449ab747fSPaolo Bonzini     /* Command set (AMD/Fujitsu) */
62549ab747fSPaolo Bonzini     pfl->cfi_table[0x13] = 0x02;
62649ab747fSPaolo Bonzini     pfl->cfi_table[0x14] = 0x00;
62749ab747fSPaolo Bonzini     /* Primary extended table address */
62849ab747fSPaolo Bonzini     pfl->cfi_table[0x15] = 0x31;
62949ab747fSPaolo Bonzini     pfl->cfi_table[0x16] = 0x00;
63049ab747fSPaolo Bonzini     /* Alternate command set (none) */
63149ab747fSPaolo Bonzini     pfl->cfi_table[0x17] = 0x00;
63249ab747fSPaolo Bonzini     pfl->cfi_table[0x18] = 0x00;
63349ab747fSPaolo Bonzini     /* Alternate extended table (none) */
63449ab747fSPaolo Bonzini     pfl->cfi_table[0x19] = 0x00;
63549ab747fSPaolo Bonzini     pfl->cfi_table[0x1A] = 0x00;
63649ab747fSPaolo Bonzini     /* Vcc min */
63749ab747fSPaolo Bonzini     pfl->cfi_table[0x1B] = 0x27;
63849ab747fSPaolo Bonzini     /* Vcc max */
63949ab747fSPaolo Bonzini     pfl->cfi_table[0x1C] = 0x36;
64049ab747fSPaolo Bonzini     /* Vpp min (no Vpp pin) */
64149ab747fSPaolo Bonzini     pfl->cfi_table[0x1D] = 0x00;
64249ab747fSPaolo Bonzini     /* Vpp max (no Vpp pin) */
64349ab747fSPaolo Bonzini     pfl->cfi_table[0x1E] = 0x00;
64449ab747fSPaolo Bonzini     /* Reserved */
64549ab747fSPaolo Bonzini     pfl->cfi_table[0x1F] = 0x07;
64649ab747fSPaolo Bonzini     /* Timeout for min size buffer write (NA) */
64749ab747fSPaolo Bonzini     pfl->cfi_table[0x20] = 0x00;
64849ab747fSPaolo Bonzini     /* Typical timeout for block erase (512 ms) */
64949ab747fSPaolo Bonzini     pfl->cfi_table[0x21] = 0x09;
65049ab747fSPaolo Bonzini     /* Typical timeout for full chip erase (4096 ms) */
65149ab747fSPaolo Bonzini     pfl->cfi_table[0x22] = 0x0C;
65249ab747fSPaolo Bonzini     /* Reserved */
65349ab747fSPaolo Bonzini     pfl->cfi_table[0x23] = 0x01;
65449ab747fSPaolo Bonzini     /* Max timeout for buffer write (NA) */
65549ab747fSPaolo Bonzini     pfl->cfi_table[0x24] = 0x00;
65649ab747fSPaolo Bonzini     /* Max timeout for block erase */
65749ab747fSPaolo Bonzini     pfl->cfi_table[0x25] = 0x0A;
65849ab747fSPaolo Bonzini     /* Max timeout for chip erase */
65949ab747fSPaolo Bonzini     pfl->cfi_table[0x26] = 0x0D;
66049ab747fSPaolo Bonzini     /* Device size */
66149ab747fSPaolo Bonzini     pfl->cfi_table[0x27] = ctz32(chip_len);
66249ab747fSPaolo Bonzini     /* Flash device interface (8 & 16 bits) */
66349ab747fSPaolo Bonzini     pfl->cfi_table[0x28] = 0x02;
66449ab747fSPaolo Bonzini     pfl->cfi_table[0x29] = 0x00;
66549ab747fSPaolo Bonzini     /* Max number of bytes in multi-bytes write */
66649ab747fSPaolo Bonzini     /* XXX: disable buffered write as it's not supported */
66749ab747fSPaolo Bonzini     //    pfl->cfi_table[0x2A] = 0x05;
66849ab747fSPaolo Bonzini     pfl->cfi_table[0x2A] = 0x00;
66949ab747fSPaolo Bonzini     pfl->cfi_table[0x2B] = 0x00;
67049ab747fSPaolo Bonzini     /* Number of erase block regions (uniform) */
67149ab747fSPaolo Bonzini     pfl->cfi_table[0x2C] = 0x01;
67249ab747fSPaolo Bonzini     /* Erase block region 1 */
67349ab747fSPaolo Bonzini     pfl->cfi_table[0x2D] = pfl->nb_blocs - 1;
67449ab747fSPaolo Bonzini     pfl->cfi_table[0x2E] = (pfl->nb_blocs - 1) >> 8;
67549ab747fSPaolo Bonzini     pfl->cfi_table[0x2F] = pfl->sector_len >> 8;
67649ab747fSPaolo Bonzini     pfl->cfi_table[0x30] = pfl->sector_len >> 16;
67749ab747fSPaolo Bonzini 
67849ab747fSPaolo Bonzini     /* Extended */
67949ab747fSPaolo Bonzini     pfl->cfi_table[0x31] = 'P';
68049ab747fSPaolo Bonzini     pfl->cfi_table[0x32] = 'R';
68149ab747fSPaolo Bonzini     pfl->cfi_table[0x33] = 'I';
68249ab747fSPaolo Bonzini 
68349ab747fSPaolo Bonzini     pfl->cfi_table[0x34] = '1';
68449ab747fSPaolo Bonzini     pfl->cfi_table[0x35] = '0';
68549ab747fSPaolo Bonzini 
68649ab747fSPaolo Bonzini     pfl->cfi_table[0x36] = 0x00;
68749ab747fSPaolo Bonzini     pfl->cfi_table[0x37] = 0x00;
68849ab747fSPaolo Bonzini     pfl->cfi_table[0x38] = 0x00;
68949ab747fSPaolo Bonzini     pfl->cfi_table[0x39] = 0x00;
69049ab747fSPaolo Bonzini 
69149ab747fSPaolo Bonzini     pfl->cfi_table[0x3a] = 0x00;
69249ab747fSPaolo Bonzini 
69349ab747fSPaolo Bonzini     pfl->cfi_table[0x3b] = 0x00;
69449ab747fSPaolo Bonzini     pfl->cfi_table[0x3c] = 0x00;
69549ab747fSPaolo Bonzini }
69649ab747fSPaolo Bonzini 
69749ab747fSPaolo Bonzini static Property pflash_cfi02_properties[] = {
69816434065SMarkus Armbruster     DEFINE_PROP_DRIVE("drive", PFlashCFI02, blk),
69916434065SMarkus Armbruster     DEFINE_PROP_UINT32("num-blocks", PFlashCFI02, nb_blocs, 0),
70016434065SMarkus Armbruster     DEFINE_PROP_UINT32("sector-length", PFlashCFI02, sector_len, 0),
70116434065SMarkus Armbruster     DEFINE_PROP_UINT8("width", PFlashCFI02, width, 0),
70216434065SMarkus Armbruster     DEFINE_PROP_UINT8("mappings", PFlashCFI02, mappings, 0),
70316434065SMarkus Armbruster     DEFINE_PROP_UINT8("big-endian", PFlashCFI02, be, 0),
70416434065SMarkus Armbruster     DEFINE_PROP_UINT16("id0", PFlashCFI02, ident0, 0),
70516434065SMarkus Armbruster     DEFINE_PROP_UINT16("id1", PFlashCFI02, ident1, 0),
70616434065SMarkus Armbruster     DEFINE_PROP_UINT16("id2", PFlashCFI02, ident2, 0),
70716434065SMarkus Armbruster     DEFINE_PROP_UINT16("id3", PFlashCFI02, ident3, 0),
70816434065SMarkus Armbruster     DEFINE_PROP_UINT16("unlock-addr0", PFlashCFI02, unlock_addr0, 0),
70916434065SMarkus Armbruster     DEFINE_PROP_UINT16("unlock-addr1", PFlashCFI02, unlock_addr1, 0),
71016434065SMarkus Armbruster     DEFINE_PROP_STRING("name", PFlashCFI02, name),
71149ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
71249ab747fSPaolo Bonzini };
71349ab747fSPaolo Bonzini 
714d80cf1ebSStephen Checkoway static void pflash_cfi02_unrealize(DeviceState *dev, Error **errp)
715d80cf1ebSStephen Checkoway {
716e7b62741SMarkus Armbruster     PFlashCFI02 *pfl = PFLASH_CFI02(dev);
717d80cf1ebSStephen Checkoway     timer_del(&pfl->timer);
718d80cf1ebSStephen Checkoway }
719d80cf1ebSStephen Checkoway 
72049ab747fSPaolo Bonzini static void pflash_cfi02_class_init(ObjectClass *klass, void *data)
72149ab747fSPaolo Bonzini {
72249ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
72349ab747fSPaolo Bonzini 
724da3bd642SHu Tao     dc->realize = pflash_cfi02_realize;
725d80cf1ebSStephen Checkoway     dc->unrealize = pflash_cfi02_unrealize;
72649ab747fSPaolo Bonzini     dc->props = pflash_cfi02_properties;
727df6f9318SAntony Pavlov     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
72849ab747fSPaolo Bonzini }
72949ab747fSPaolo Bonzini 
73049ab747fSPaolo Bonzini static const TypeInfo pflash_cfi02_info = {
731e7b62741SMarkus Armbruster     .name           = TYPE_PFLASH_CFI02,
73249ab747fSPaolo Bonzini     .parent         = TYPE_SYS_BUS_DEVICE,
73316434065SMarkus Armbruster     .instance_size  = sizeof(PFlashCFI02),
73449ab747fSPaolo Bonzini     .class_init     = pflash_cfi02_class_init,
73549ab747fSPaolo Bonzini };
73649ab747fSPaolo Bonzini 
73749ab747fSPaolo Bonzini static void pflash_cfi02_register_types(void)
73849ab747fSPaolo Bonzini {
73949ab747fSPaolo Bonzini     type_register_static(&pflash_cfi02_info);
74049ab747fSPaolo Bonzini }
74149ab747fSPaolo Bonzini 
74249ab747fSPaolo Bonzini type_init(pflash_cfi02_register_types)
74349ab747fSPaolo Bonzini 
74416434065SMarkus Armbruster PFlashCFI02 *pflash_cfi02_register(hwaddr base,
745940d5b13SMarkus Armbruster                                    const char *name,
74649ab747fSPaolo Bonzini                                    hwaddr size,
74716434065SMarkus Armbruster                                    BlockBackend *blk,
748ce14710fSMarkus Armbruster                                    uint32_t sector_len,
74916434065SMarkus Armbruster                                    int nb_mappings, int width,
75049ab747fSPaolo Bonzini                                    uint16_t id0, uint16_t id1,
75149ab747fSPaolo Bonzini                                    uint16_t id2, uint16_t id3,
75216434065SMarkus Armbruster                                    uint16_t unlock_addr0,
75316434065SMarkus Armbruster                                    uint16_t unlock_addr1,
75449ab747fSPaolo Bonzini                                    int be)
75549ab747fSPaolo Bonzini {
756e7b62741SMarkus Armbruster     DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI02);
75749ab747fSPaolo Bonzini 
7589b3d111aSMarkus Armbruster     if (blk) {
7599b3d111aSMarkus Armbruster         qdev_prop_set_drive(dev, "drive", blk, &error_abort);
76049ab747fSPaolo Bonzini     }
761ce14710fSMarkus Armbruster     assert(size % sector_len == 0);
762ce14710fSMarkus Armbruster     qdev_prop_set_uint32(dev, "num-blocks", size / sector_len);
76349ab747fSPaolo Bonzini     qdev_prop_set_uint32(dev, "sector-length", sector_len);
76449ab747fSPaolo Bonzini     qdev_prop_set_uint8(dev, "width", width);
76549ab747fSPaolo Bonzini     qdev_prop_set_uint8(dev, "mappings", nb_mappings);
76649ab747fSPaolo Bonzini     qdev_prop_set_uint8(dev, "big-endian", !!be);
76749ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id0", id0);
76849ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id1", id1);
76949ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id2", id2);
77049ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id3", id3);
77149ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "unlock-addr0", unlock_addr0);
77249ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "unlock-addr1", unlock_addr1);
77349ab747fSPaolo Bonzini     qdev_prop_set_string(dev, "name", name);
77449ab747fSPaolo Bonzini     qdev_init_nofail(dev);
77549ab747fSPaolo Bonzini 
7763509c396SHu Tao     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
777e7b62741SMarkus Armbruster     return PFLASH_CFI02(dev);
77849ab747fSPaolo Bonzini }
779