xref: /qemu/hw/block/pflash_cfi02.c (revision 06e8b8e3)
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 
168*06e8b8e3SPhilippe Mathieu-Daudé /*
169*06e8b8e3SPhilippe Mathieu-Daudé  * Read data from flash.
170*06e8b8e3SPhilippe Mathieu-Daudé  */
171*06e8b8e3SPhilippe Mathieu-Daudé static uint64_t pflash_data_read(PFlashCFI02 *pfl, hwaddr offset,
172*06e8b8e3SPhilippe Mathieu-Daudé                                  unsigned int width)
173*06e8b8e3SPhilippe Mathieu-Daudé {
174*06e8b8e3SPhilippe Mathieu-Daudé     uint8_t *p = (uint8_t *)pfl->storage + offset;
175*06e8b8e3SPhilippe Mathieu-Daudé     uint64_t ret = pfl->be ? ldn_be_p(p, width) : ldn_le_p(p, width);
176*06e8b8e3SPhilippe Mathieu-Daudé     trace_pflash_data_read(offset, width << 1, ret);
177*06e8b8e3SPhilippe Mathieu-Daudé     return ret;
178*06e8b8e3SPhilippe Mathieu-Daudé }
179*06e8b8e3SPhilippe Mathieu-Daudé 
18016434065SMarkus Armbruster static uint32_t pflash_read(PFlashCFI02 *pfl, hwaddr offset,
18149ab747fSPaolo Bonzini                             int width, int be)
18249ab747fSPaolo Bonzini {
18349ab747fSPaolo Bonzini     hwaddr boff;
18449ab747fSPaolo Bonzini     uint32_t ret;
18549ab747fSPaolo Bonzini 
18649ab747fSPaolo Bonzini     ret = -1;
18749ab747fSPaolo Bonzini     /* Lazy reset to ROMD mode after a certain amount of read accesses */
18849ab747fSPaolo Bonzini     if (!pfl->rom_mode && pfl->wcycle == 0 &&
18949ab747fSPaolo Bonzini         ++pfl->read_counter > PFLASH_LAZY_ROMD_THRESHOLD) {
19049ab747fSPaolo Bonzini         pflash_register_memory(pfl, 1);
19149ab747fSPaolo Bonzini     }
19249ab747fSPaolo Bonzini     offset &= pfl->chip_len - 1;
19349ab747fSPaolo Bonzini     boff = offset & 0xFF;
19449ab747fSPaolo Bonzini     if (pfl->width == 2)
19549ab747fSPaolo Bonzini         boff = boff >> 1;
19649ab747fSPaolo Bonzini     else if (pfl->width == 4)
19749ab747fSPaolo Bonzini         boff = boff >> 2;
19849ab747fSPaolo Bonzini     switch (pfl->cmd) {
19949ab747fSPaolo Bonzini     default:
20049ab747fSPaolo Bonzini         /* This should never happen : reset state & treat it as a read*/
20149ab747fSPaolo Bonzini         DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd);
20249ab747fSPaolo Bonzini         pfl->wcycle = 0;
20349ab747fSPaolo Bonzini         pfl->cmd = 0;
20449ab747fSPaolo Bonzini         /* fall through to the read code */
20549ab747fSPaolo Bonzini     case 0x80:
20649ab747fSPaolo Bonzini         /* We accept reads during second unlock sequence... */
20749ab747fSPaolo Bonzini     case 0x00:
20849ab747fSPaolo Bonzini         /* Flash area read */
209*06e8b8e3SPhilippe Mathieu-Daudé         ret = pflash_data_read(pfl, offset, width);
21049ab747fSPaolo Bonzini         break;
21149ab747fSPaolo Bonzini     case 0x90:
21249ab747fSPaolo Bonzini         /* flash ID read */
21349ab747fSPaolo Bonzini         switch (boff) {
21449ab747fSPaolo Bonzini         case 0x00:
21549ab747fSPaolo Bonzini         case 0x01:
21649ab747fSPaolo Bonzini             ret = boff & 0x01 ? pfl->ident1 : pfl->ident0;
21749ab747fSPaolo Bonzini             break;
21849ab747fSPaolo Bonzini         case 0x02:
21949ab747fSPaolo Bonzini             ret = 0x00; /* Pretend all sectors are unprotected */
22049ab747fSPaolo Bonzini             break;
22149ab747fSPaolo Bonzini         case 0x0E:
22249ab747fSPaolo Bonzini         case 0x0F:
22349ab747fSPaolo Bonzini             ret = boff & 0x01 ? pfl->ident3 : pfl->ident2;
2247f7bdcafSPhilippe Mathieu-Daudé             if (ret != (uint8_t)-1) {
22549ab747fSPaolo Bonzini                 break;
2267f7bdcafSPhilippe Mathieu-Daudé             }
2277f7bdcafSPhilippe Mathieu-Daudé             /* Fall through to data read. */
22849ab747fSPaolo Bonzini         default:
229*06e8b8e3SPhilippe Mathieu-Daudé             ret = pflash_data_read(pfl, offset, width);
23049ab747fSPaolo Bonzini         }
2316536987fSPhilippe Mathieu-Daudé         DPRINTF("%s: ID " TARGET_FMT_plx " %" PRIx32 "\n", __func__, boff, ret);
23249ab747fSPaolo Bonzini         break;
23349ab747fSPaolo Bonzini     case 0xA0:
23449ab747fSPaolo Bonzini     case 0x10:
23549ab747fSPaolo Bonzini     case 0x30:
23649ab747fSPaolo Bonzini         /* Status register read */
23749ab747fSPaolo Bonzini         ret = pfl->status;
2386536987fSPhilippe Mathieu-Daudé         DPRINTF("%s: status %" PRIx32 "\n", __func__, ret);
2391d311e73SPhilippe Mathieu-Daudé         toggle_dq6(pfl);
24049ab747fSPaolo Bonzini         break;
24149ab747fSPaolo Bonzini     case 0x98:
24249ab747fSPaolo Bonzini         /* CFI query mode */
24307c13a71SPhilippe Mathieu-Daudé         if (boff < sizeof(pfl->cfi_table)) {
24449ab747fSPaolo Bonzini             ret = pfl->cfi_table[boff];
24507c13a71SPhilippe Mathieu-Daudé         } else {
24607c13a71SPhilippe Mathieu-Daudé             ret = 0;
24707c13a71SPhilippe Mathieu-Daudé         }
24849ab747fSPaolo Bonzini         break;
24949ab747fSPaolo Bonzini     }
250e8aa2d95SPhilippe Mathieu-Daudé     trace_pflash_io_read(offset, width, width << 1, ret, pfl->cmd, pfl->wcycle);
25149ab747fSPaolo Bonzini 
25249ab747fSPaolo Bonzini     return ret;
25349ab747fSPaolo Bonzini }
25449ab747fSPaolo Bonzini 
25549ab747fSPaolo Bonzini /* update flash content on disk */
25616434065SMarkus Armbruster static void pflash_update(PFlashCFI02 *pfl, int offset,
25749ab747fSPaolo Bonzini                           int size)
25849ab747fSPaolo Bonzini {
25949ab747fSPaolo Bonzini     int offset_end;
2604be74634SMarkus Armbruster     if (pfl->blk) {
26149ab747fSPaolo Bonzini         offset_end = offset + size;
262098e732dSEric Blake         /* widen to sector boundaries */
263098e732dSEric Blake         offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE);
264098e732dSEric Blake         offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE);
265098e732dSEric Blake         blk_pwrite(pfl->blk, offset, pfl->storage + offset,
266098e732dSEric Blake                    offset_end - offset, 0);
26749ab747fSPaolo Bonzini     }
26849ab747fSPaolo Bonzini }
26949ab747fSPaolo Bonzini 
27016434065SMarkus Armbruster static void pflash_write(PFlashCFI02 *pfl, hwaddr offset,
27149ab747fSPaolo Bonzini                          uint32_t value, int width, int be)
27249ab747fSPaolo Bonzini {
27349ab747fSPaolo Bonzini     hwaddr boff;
27449ab747fSPaolo Bonzini     uint8_t *p;
27549ab747fSPaolo Bonzini     uint8_t cmd;
27649ab747fSPaolo Bonzini 
277e8aa2d95SPhilippe Mathieu-Daudé     trace_pflash_io_write(offset, width, width << 1, value, pfl->wcycle);
27849ab747fSPaolo Bonzini     cmd = value;
27949ab747fSPaolo Bonzini     if (pfl->cmd != 0xA0 && cmd == 0xF0) {
28049ab747fSPaolo Bonzini         goto reset_flash;
28149ab747fSPaolo Bonzini     }
28249ab747fSPaolo Bonzini     offset &= pfl->chip_len - 1;
28349ab747fSPaolo Bonzini 
28449ab747fSPaolo Bonzini     boff = offset & (pfl->sector_len - 1);
28549ab747fSPaolo Bonzini     if (pfl->width == 2)
28649ab747fSPaolo Bonzini         boff = boff >> 1;
28749ab747fSPaolo Bonzini     else if (pfl->width == 4)
28849ab747fSPaolo Bonzini         boff = boff >> 2;
28949ab747fSPaolo Bonzini     switch (pfl->wcycle) {
29049ab747fSPaolo Bonzini     case 0:
29149ab747fSPaolo Bonzini         /* Set the device in I/O access mode if required */
29249ab747fSPaolo Bonzini         if (pfl->rom_mode)
29349ab747fSPaolo Bonzini             pflash_register_memory(pfl, 0);
29449ab747fSPaolo Bonzini         pfl->read_counter = 0;
29549ab747fSPaolo Bonzini         /* We're in read mode */
29649ab747fSPaolo Bonzini     check_unlock0:
29749ab747fSPaolo Bonzini         if (boff == 0x55 && cmd == 0x98) {
29849ab747fSPaolo Bonzini         enter_CFI_mode:
29949ab747fSPaolo Bonzini             /* Enter CFI query mode */
300aeaf6c20SPhilippe Mathieu-Daudé             pfl->wcycle = WCYCLE_CFI;
30149ab747fSPaolo Bonzini             pfl->cmd = 0x98;
30249ab747fSPaolo Bonzini             return;
30349ab747fSPaolo Bonzini         }
30449ab747fSPaolo Bonzini         if (boff != pfl->unlock_addr0 || cmd != 0xAA) {
30549ab747fSPaolo Bonzini             DPRINTF("%s: unlock0 failed " TARGET_FMT_plx " %02x %04x\n",
30649ab747fSPaolo Bonzini                     __func__, boff, cmd, pfl->unlock_addr0);
30749ab747fSPaolo Bonzini             goto reset_flash;
30849ab747fSPaolo Bonzini         }
30949ab747fSPaolo Bonzini         DPRINTF("%s: unlock sequence started\n", __func__);
31049ab747fSPaolo Bonzini         break;
31149ab747fSPaolo Bonzini     case 1:
31249ab747fSPaolo Bonzini         /* We started an unlock sequence */
31349ab747fSPaolo Bonzini     check_unlock1:
31449ab747fSPaolo Bonzini         if (boff != pfl->unlock_addr1 || cmd != 0x55) {
31549ab747fSPaolo Bonzini             DPRINTF("%s: unlock1 failed " TARGET_FMT_plx " %02x\n", __func__,
31649ab747fSPaolo Bonzini                     boff, cmd);
31749ab747fSPaolo Bonzini             goto reset_flash;
31849ab747fSPaolo Bonzini         }
31949ab747fSPaolo Bonzini         DPRINTF("%s: unlock sequence done\n", __func__);
32049ab747fSPaolo Bonzini         break;
32149ab747fSPaolo Bonzini     case 2:
32249ab747fSPaolo Bonzini         /* We finished an unlock sequence */
32349ab747fSPaolo Bonzini         if (!pfl->bypass && boff != pfl->unlock_addr0) {
32449ab747fSPaolo Bonzini             DPRINTF("%s: command failed " TARGET_FMT_plx " %02x\n", __func__,
32549ab747fSPaolo Bonzini                     boff, cmd);
32649ab747fSPaolo Bonzini             goto reset_flash;
32749ab747fSPaolo Bonzini         }
32849ab747fSPaolo Bonzini         switch (cmd) {
32949ab747fSPaolo Bonzini         case 0x20:
33049ab747fSPaolo Bonzini             pfl->bypass = 1;
33149ab747fSPaolo Bonzini             goto do_bypass;
33249ab747fSPaolo Bonzini         case 0x80:
33349ab747fSPaolo Bonzini         case 0x90:
33449ab747fSPaolo Bonzini         case 0xA0:
33549ab747fSPaolo Bonzini             pfl->cmd = cmd;
33649ab747fSPaolo Bonzini             DPRINTF("%s: starting command %02x\n", __func__, cmd);
33749ab747fSPaolo Bonzini             break;
33849ab747fSPaolo Bonzini         default:
33949ab747fSPaolo Bonzini             DPRINTF("%s: unknown command %02x\n", __func__, cmd);
34049ab747fSPaolo Bonzini             goto reset_flash;
34149ab747fSPaolo Bonzini         }
34249ab747fSPaolo Bonzini         break;
34349ab747fSPaolo Bonzini     case 3:
34449ab747fSPaolo Bonzini         switch (pfl->cmd) {
34549ab747fSPaolo Bonzini         case 0x80:
34649ab747fSPaolo Bonzini             /* We need another unlock sequence */
34749ab747fSPaolo Bonzini             goto check_unlock0;
34849ab747fSPaolo Bonzini         case 0xA0:
349c1474acdSPhilippe Mathieu-Daudé             trace_pflash_data_write(offset, width << 1, value, 0);
35049ab747fSPaolo Bonzini             if (!pfl->ro) {
351c3d25271SPhilippe Mathieu-Daudé                 p = (uint8_t *)pfl->storage + offset;
352c3d25271SPhilippe Mathieu-Daudé                 if (pfl->be) {
353c3d25271SPhilippe Mathieu-Daudé                     uint64_t current = ldn_be_p(p, width);
354c3d25271SPhilippe Mathieu-Daudé                     stn_be_p(p, width, current & value);
35549ab747fSPaolo Bonzini                 } else {
356c3d25271SPhilippe Mathieu-Daudé                     uint64_t current = ldn_le_p(p, width);
357c3d25271SPhilippe Mathieu-Daudé                     stn_le_p(p, width, current & value);
35849ab747fSPaolo Bonzini                 }
359c3d25271SPhilippe Mathieu-Daudé                 pflash_update(pfl, offset, width);
36049ab747fSPaolo Bonzini             }
3611d311e73SPhilippe Mathieu-Daudé             /*
3621d311e73SPhilippe Mathieu-Daudé              * While programming, status bit DQ7 should hold the opposite
3631d311e73SPhilippe Mathieu-Daudé              * value from how it was programmed.
3641d311e73SPhilippe Mathieu-Daudé              */
3651d311e73SPhilippe Mathieu-Daudé             set_dq7(pfl, ~value);
36649ab747fSPaolo Bonzini             /* Let's pretend write is immediate */
36749ab747fSPaolo Bonzini             if (pfl->bypass)
36849ab747fSPaolo Bonzini                 goto do_bypass;
36949ab747fSPaolo Bonzini             goto reset_flash;
37049ab747fSPaolo Bonzini         case 0x90:
37149ab747fSPaolo Bonzini             if (pfl->bypass && cmd == 0x00) {
37249ab747fSPaolo Bonzini                 /* Unlock bypass reset */
37349ab747fSPaolo Bonzini                 goto reset_flash;
37449ab747fSPaolo Bonzini             }
37549ab747fSPaolo Bonzini             /* We can enter CFI query mode from autoselect mode */
37649ab747fSPaolo Bonzini             if (boff == 0x55 && cmd == 0x98)
37749ab747fSPaolo Bonzini                 goto enter_CFI_mode;
37849ab747fSPaolo Bonzini             /* No break here */
37949ab747fSPaolo Bonzini         default:
38049ab747fSPaolo Bonzini             DPRINTF("%s: invalid write for command %02x\n",
38149ab747fSPaolo Bonzini                     __func__, pfl->cmd);
38249ab747fSPaolo Bonzini             goto reset_flash;
38349ab747fSPaolo Bonzini         }
38449ab747fSPaolo Bonzini     case 4:
38549ab747fSPaolo Bonzini         switch (pfl->cmd) {
38649ab747fSPaolo Bonzini         case 0xA0:
38749ab747fSPaolo Bonzini             /* Ignore writes while flash data write is occurring */
38849ab747fSPaolo Bonzini             /* As we suppose write is immediate, this should never happen */
38949ab747fSPaolo Bonzini             return;
39049ab747fSPaolo Bonzini         case 0x80:
39149ab747fSPaolo Bonzini             goto check_unlock1;
39249ab747fSPaolo Bonzini         default:
39349ab747fSPaolo Bonzini             /* Should never happen */
39449ab747fSPaolo Bonzini             DPRINTF("%s: invalid command state %02x (wc 4)\n",
39549ab747fSPaolo Bonzini                     __func__, pfl->cmd);
39649ab747fSPaolo Bonzini             goto reset_flash;
39749ab747fSPaolo Bonzini         }
39849ab747fSPaolo Bonzini         break;
39949ab747fSPaolo Bonzini     case 5:
40049ab747fSPaolo Bonzini         switch (cmd) {
40149ab747fSPaolo Bonzini         case 0x10:
40249ab747fSPaolo Bonzini             if (boff != pfl->unlock_addr0) {
40349ab747fSPaolo Bonzini                 DPRINTF("%s: chip erase: invalid address " TARGET_FMT_plx "\n",
40449ab747fSPaolo Bonzini                         __func__, offset);
40549ab747fSPaolo Bonzini                 goto reset_flash;
40649ab747fSPaolo Bonzini             }
40749ab747fSPaolo Bonzini             /* Chip erase */
40849ab747fSPaolo Bonzini             DPRINTF("%s: start chip erase\n", __func__);
40949ab747fSPaolo Bonzini             if (!pfl->ro) {
41049ab747fSPaolo Bonzini                 memset(pfl->storage, 0xFF, pfl->chip_len);
41149ab747fSPaolo Bonzini                 pflash_update(pfl, 0, pfl->chip_len);
41249ab747fSPaolo Bonzini             }
4131d311e73SPhilippe Mathieu-Daudé             set_dq7(pfl, 0x00);
41449ab747fSPaolo Bonzini             /* Let's wait 5 seconds before chip erase is done */
415d80cf1ebSStephen Checkoway             timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
41673bcb24dSRutuja Shah                       (NANOSECONDS_PER_SECOND * 5));
41749ab747fSPaolo Bonzini             break;
41849ab747fSPaolo Bonzini         case 0x30:
41949ab747fSPaolo Bonzini             /* Sector erase */
42049ab747fSPaolo Bonzini             p = pfl->storage;
42149ab747fSPaolo Bonzini             offset &= ~(pfl->sector_len - 1);
42249ab747fSPaolo Bonzini             DPRINTF("%s: start sector erase at " TARGET_FMT_plx "\n", __func__,
42349ab747fSPaolo Bonzini                     offset);
42449ab747fSPaolo Bonzini             if (!pfl->ro) {
42549ab747fSPaolo Bonzini                 memset(p + offset, 0xFF, pfl->sector_len);
42649ab747fSPaolo Bonzini                 pflash_update(pfl, offset, pfl->sector_len);
42749ab747fSPaolo Bonzini             }
4281d311e73SPhilippe Mathieu-Daudé             set_dq7(pfl, 0x00);
42949ab747fSPaolo Bonzini             /* Let's wait 1/2 second before sector erase is done */
430d80cf1ebSStephen Checkoway             timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
43173bcb24dSRutuja Shah                       (NANOSECONDS_PER_SECOND / 2));
43249ab747fSPaolo Bonzini             break;
43349ab747fSPaolo Bonzini         default:
43449ab747fSPaolo Bonzini             DPRINTF("%s: invalid command %02x (wc 5)\n", __func__, cmd);
43549ab747fSPaolo Bonzini             goto reset_flash;
43649ab747fSPaolo Bonzini         }
43749ab747fSPaolo Bonzini         pfl->cmd = cmd;
43849ab747fSPaolo Bonzini         break;
43949ab747fSPaolo Bonzini     case 6:
44049ab747fSPaolo Bonzini         switch (pfl->cmd) {
44149ab747fSPaolo Bonzini         case 0x10:
44249ab747fSPaolo Bonzini             /* Ignore writes during chip erase */
44349ab747fSPaolo Bonzini             return;
44449ab747fSPaolo Bonzini         case 0x30:
44549ab747fSPaolo Bonzini             /* Ignore writes during sector erase */
44649ab747fSPaolo Bonzini             return;
44749ab747fSPaolo Bonzini         default:
44849ab747fSPaolo Bonzini             /* Should never happen */
44949ab747fSPaolo Bonzini             DPRINTF("%s: invalid command state %02x (wc 6)\n",
45049ab747fSPaolo Bonzini                     __func__, pfl->cmd);
45149ab747fSPaolo Bonzini             goto reset_flash;
45249ab747fSPaolo Bonzini         }
45349ab747fSPaolo Bonzini         break;
454aeaf6c20SPhilippe Mathieu-Daudé     /* Special values for CFI queries */
455aeaf6c20SPhilippe Mathieu-Daudé     case WCYCLE_CFI:
45649ab747fSPaolo Bonzini         DPRINTF("%s: invalid write in CFI query mode\n", __func__);
45749ab747fSPaolo Bonzini         goto reset_flash;
45849ab747fSPaolo Bonzini     default:
45949ab747fSPaolo Bonzini         /* Should never happen */
46049ab747fSPaolo Bonzini         DPRINTF("%s: invalid write state (wc 7)\n",  __func__);
46149ab747fSPaolo Bonzini         goto reset_flash;
46249ab747fSPaolo Bonzini     }
46349ab747fSPaolo Bonzini     pfl->wcycle++;
46449ab747fSPaolo Bonzini 
46549ab747fSPaolo Bonzini     return;
46649ab747fSPaolo Bonzini 
46749ab747fSPaolo Bonzini     /* Reset flash */
46849ab747fSPaolo Bonzini  reset_flash:
46913019f1fSPhilippe Mathieu-Daudé     trace_pflash_reset();
47049ab747fSPaolo Bonzini     pfl->bypass = 0;
47149ab747fSPaolo Bonzini     pfl->wcycle = 0;
47249ab747fSPaolo Bonzini     pfl->cmd = 0;
47349ab747fSPaolo Bonzini     return;
47449ab747fSPaolo Bonzini 
47549ab747fSPaolo Bonzini  do_bypass:
47649ab747fSPaolo Bonzini     pfl->wcycle = 2;
47749ab747fSPaolo Bonzini     pfl->cmd = 0;
47849ab747fSPaolo Bonzini }
47949ab747fSPaolo Bonzini 
480a4afb28dSPeter Maydell static uint64_t pflash_be_readfn(void *opaque, hwaddr addr, unsigned size)
48149ab747fSPaolo Bonzini {
482a4afb28dSPeter Maydell     return pflash_read(opaque, addr, size, 1);
48349ab747fSPaolo Bonzini }
48449ab747fSPaolo Bonzini 
485a4afb28dSPeter Maydell static void pflash_be_writefn(void *opaque, hwaddr addr,
486a4afb28dSPeter Maydell                               uint64_t value, unsigned size)
48749ab747fSPaolo Bonzini {
488a4afb28dSPeter Maydell     pflash_write(opaque, addr, value, size, 1);
48949ab747fSPaolo Bonzini }
49049ab747fSPaolo Bonzini 
491a4afb28dSPeter Maydell static uint64_t pflash_le_readfn(void *opaque, hwaddr addr, unsigned size)
49249ab747fSPaolo Bonzini {
493a4afb28dSPeter Maydell     return pflash_read(opaque, addr, size, 0);
49449ab747fSPaolo Bonzini }
49549ab747fSPaolo Bonzini 
496a4afb28dSPeter Maydell static void pflash_le_writefn(void *opaque, hwaddr addr,
497a4afb28dSPeter Maydell                               uint64_t value, unsigned size)
49849ab747fSPaolo Bonzini {
499a4afb28dSPeter Maydell     pflash_write(opaque, addr, value, size, 0);
50049ab747fSPaolo Bonzini }
50149ab747fSPaolo Bonzini 
50249ab747fSPaolo Bonzini static const MemoryRegionOps pflash_cfi02_ops_be = {
503a4afb28dSPeter Maydell     .read = pflash_be_readfn,
504a4afb28dSPeter Maydell     .write = pflash_be_writefn,
505a4afb28dSPeter Maydell     .valid.min_access_size = 1,
506a4afb28dSPeter Maydell     .valid.max_access_size = 4,
50749ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
50849ab747fSPaolo Bonzini };
50949ab747fSPaolo Bonzini 
51049ab747fSPaolo Bonzini static const MemoryRegionOps pflash_cfi02_ops_le = {
511a4afb28dSPeter Maydell     .read = pflash_le_readfn,
512a4afb28dSPeter Maydell     .write = pflash_le_writefn,
513a4afb28dSPeter Maydell     .valid.min_access_size = 1,
514a4afb28dSPeter Maydell     .valid.max_access_size = 4,
51549ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
51649ab747fSPaolo Bonzini };
51749ab747fSPaolo Bonzini 
518da3bd642SHu Tao static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
51949ab747fSPaolo Bonzini {
520e7b62741SMarkus Armbruster     PFlashCFI02 *pfl = PFLASH_CFI02(dev);
52149ab747fSPaolo Bonzini     uint32_t chip_len;
52249ab747fSPaolo Bonzini     int ret;
52333e0eb52SHu Tao     Error *local_err = NULL;
52449ab747fSPaolo Bonzini 
5258929fc3aSZiyue Yang     if (pfl->sector_len == 0) {
5268929fc3aSZiyue Yang         error_setg(errp, "attribute \"sector-length\" not specified or zero.");
5278929fc3aSZiyue Yang         return;
5288929fc3aSZiyue Yang     }
5298929fc3aSZiyue Yang     if (pfl->nb_blocs == 0) {
5308929fc3aSZiyue Yang         error_setg(errp, "attribute \"num-blocks\" not specified or zero.");
5318929fc3aSZiyue Yang         return;
5328929fc3aSZiyue Yang     }
5338929fc3aSZiyue Yang     if (pfl->name == NULL) {
5348929fc3aSZiyue Yang         error_setg(errp, "attribute \"name\" not specified.");
5358929fc3aSZiyue Yang         return;
5368929fc3aSZiyue Yang     }
5378929fc3aSZiyue Yang 
53849ab747fSPaolo Bonzini     chip_len = pfl->sector_len * pfl->nb_blocs;
53949ab747fSPaolo Bonzini 
540bba3ddf7SPeter Maydell     memory_region_init_rom_device(&pfl->orig_mem, OBJECT(pfl), pfl->be ?
54149ab747fSPaolo Bonzini                                   &pflash_cfi02_ops_be : &pflash_cfi02_ops_le,
54233e0eb52SHu Tao                                   pfl, pfl->name, chip_len, &local_err);
54333e0eb52SHu Tao     if (local_err) {
54433e0eb52SHu Tao         error_propagate(errp, local_err);
54533e0eb52SHu Tao         return;
54633e0eb52SHu Tao     }
54733e0eb52SHu Tao 
54849ab747fSPaolo Bonzini     pfl->storage = memory_region_get_ram_ptr(&pfl->orig_mem);
54949ab747fSPaolo Bonzini     pfl->chip_len = chip_len;
550a17c17a2SKevin Wolf 
551a17c17a2SKevin Wolf     if (pfl->blk) {
552a17c17a2SKevin Wolf         uint64_t perm;
553a17c17a2SKevin Wolf         pfl->ro = blk_is_read_only(pfl->blk);
554a17c17a2SKevin Wolf         perm = BLK_PERM_CONSISTENT_READ | (pfl->ro ? 0 : BLK_PERM_WRITE);
555a17c17a2SKevin Wolf         ret = blk_set_perm(pfl->blk, perm, BLK_PERM_ALL, errp);
556a17c17a2SKevin Wolf         if (ret < 0) {
557a17c17a2SKevin Wolf             return;
558a17c17a2SKevin Wolf         }
559a17c17a2SKevin Wolf     } else {
560a17c17a2SKevin Wolf         pfl->ro = 0;
561a17c17a2SKevin Wolf     }
562a17c17a2SKevin Wolf 
5634be74634SMarkus Armbruster     if (pfl->blk) {
56406f15217SMarkus Armbruster         if (!blk_check_size_and_read_all(pfl->blk, pfl->storage, chip_len,
56506f15217SMarkus Armbruster                                          errp)) {
566da3bd642SHu Tao             vmstate_unregister_ram(&pfl->orig_mem, DEVICE(pfl));
567da3bd642SHu Tao             return;
56849ab747fSPaolo Bonzini         }
56949ab747fSPaolo Bonzini     }
57049ab747fSPaolo Bonzini 
57149ab747fSPaolo Bonzini     pflash_setup_mappings(pfl);
57249ab747fSPaolo Bonzini     pfl->rom_mode = 1;
573da3bd642SHu Tao     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem);
57449ab747fSPaolo Bonzini 
575d80cf1ebSStephen Checkoway     timer_init_ns(&pfl->timer, QEMU_CLOCK_VIRTUAL, pflash_timer, pfl);
57649ab747fSPaolo Bonzini     pfl->wcycle = 0;
57749ab747fSPaolo Bonzini     pfl->cmd = 0;
57849ab747fSPaolo Bonzini     pfl->status = 0;
57949ab747fSPaolo Bonzini     /* Hardcoded CFI table (mostly from SG29 Spansion flash) */
58049ab747fSPaolo Bonzini     /* Standard "QRY" string */
58149ab747fSPaolo Bonzini     pfl->cfi_table[0x10] = 'Q';
58249ab747fSPaolo Bonzini     pfl->cfi_table[0x11] = 'R';
58349ab747fSPaolo Bonzini     pfl->cfi_table[0x12] = 'Y';
58449ab747fSPaolo Bonzini     /* Command set (AMD/Fujitsu) */
58549ab747fSPaolo Bonzini     pfl->cfi_table[0x13] = 0x02;
58649ab747fSPaolo Bonzini     pfl->cfi_table[0x14] = 0x00;
58749ab747fSPaolo Bonzini     /* Primary extended table address */
58849ab747fSPaolo Bonzini     pfl->cfi_table[0x15] = 0x31;
58949ab747fSPaolo Bonzini     pfl->cfi_table[0x16] = 0x00;
59049ab747fSPaolo Bonzini     /* Alternate command set (none) */
59149ab747fSPaolo Bonzini     pfl->cfi_table[0x17] = 0x00;
59249ab747fSPaolo Bonzini     pfl->cfi_table[0x18] = 0x00;
59349ab747fSPaolo Bonzini     /* Alternate extended table (none) */
59449ab747fSPaolo Bonzini     pfl->cfi_table[0x19] = 0x00;
59549ab747fSPaolo Bonzini     pfl->cfi_table[0x1A] = 0x00;
59649ab747fSPaolo Bonzini     /* Vcc min */
59749ab747fSPaolo Bonzini     pfl->cfi_table[0x1B] = 0x27;
59849ab747fSPaolo Bonzini     /* Vcc max */
59949ab747fSPaolo Bonzini     pfl->cfi_table[0x1C] = 0x36;
60049ab747fSPaolo Bonzini     /* Vpp min (no Vpp pin) */
60149ab747fSPaolo Bonzini     pfl->cfi_table[0x1D] = 0x00;
60249ab747fSPaolo Bonzini     /* Vpp max (no Vpp pin) */
60349ab747fSPaolo Bonzini     pfl->cfi_table[0x1E] = 0x00;
60449ab747fSPaolo Bonzini     /* Reserved */
60549ab747fSPaolo Bonzini     pfl->cfi_table[0x1F] = 0x07;
60649ab747fSPaolo Bonzini     /* Timeout for min size buffer write (NA) */
60749ab747fSPaolo Bonzini     pfl->cfi_table[0x20] = 0x00;
60849ab747fSPaolo Bonzini     /* Typical timeout for block erase (512 ms) */
60949ab747fSPaolo Bonzini     pfl->cfi_table[0x21] = 0x09;
61049ab747fSPaolo Bonzini     /* Typical timeout for full chip erase (4096 ms) */
61149ab747fSPaolo Bonzini     pfl->cfi_table[0x22] = 0x0C;
61249ab747fSPaolo Bonzini     /* Reserved */
61349ab747fSPaolo Bonzini     pfl->cfi_table[0x23] = 0x01;
61449ab747fSPaolo Bonzini     /* Max timeout for buffer write (NA) */
61549ab747fSPaolo Bonzini     pfl->cfi_table[0x24] = 0x00;
61649ab747fSPaolo Bonzini     /* Max timeout for block erase */
61749ab747fSPaolo Bonzini     pfl->cfi_table[0x25] = 0x0A;
61849ab747fSPaolo Bonzini     /* Max timeout for chip erase */
61949ab747fSPaolo Bonzini     pfl->cfi_table[0x26] = 0x0D;
62049ab747fSPaolo Bonzini     /* Device size */
62149ab747fSPaolo Bonzini     pfl->cfi_table[0x27] = ctz32(chip_len);
62249ab747fSPaolo Bonzini     /* Flash device interface (8 & 16 bits) */
62349ab747fSPaolo Bonzini     pfl->cfi_table[0x28] = 0x02;
62449ab747fSPaolo Bonzini     pfl->cfi_table[0x29] = 0x00;
62549ab747fSPaolo Bonzini     /* Max number of bytes in multi-bytes write */
62649ab747fSPaolo Bonzini     /* XXX: disable buffered write as it's not supported */
62749ab747fSPaolo Bonzini     //    pfl->cfi_table[0x2A] = 0x05;
62849ab747fSPaolo Bonzini     pfl->cfi_table[0x2A] = 0x00;
62949ab747fSPaolo Bonzini     pfl->cfi_table[0x2B] = 0x00;
63049ab747fSPaolo Bonzini     /* Number of erase block regions (uniform) */
63149ab747fSPaolo Bonzini     pfl->cfi_table[0x2C] = 0x01;
63249ab747fSPaolo Bonzini     /* Erase block region 1 */
63349ab747fSPaolo Bonzini     pfl->cfi_table[0x2D] = pfl->nb_blocs - 1;
63449ab747fSPaolo Bonzini     pfl->cfi_table[0x2E] = (pfl->nb_blocs - 1) >> 8;
63549ab747fSPaolo Bonzini     pfl->cfi_table[0x2F] = pfl->sector_len >> 8;
63649ab747fSPaolo Bonzini     pfl->cfi_table[0x30] = pfl->sector_len >> 16;
63749ab747fSPaolo Bonzini 
63849ab747fSPaolo Bonzini     /* Extended */
63949ab747fSPaolo Bonzini     pfl->cfi_table[0x31] = 'P';
64049ab747fSPaolo Bonzini     pfl->cfi_table[0x32] = 'R';
64149ab747fSPaolo Bonzini     pfl->cfi_table[0x33] = 'I';
64249ab747fSPaolo Bonzini 
64349ab747fSPaolo Bonzini     pfl->cfi_table[0x34] = '1';
64449ab747fSPaolo Bonzini     pfl->cfi_table[0x35] = '0';
64549ab747fSPaolo Bonzini 
64649ab747fSPaolo Bonzini     pfl->cfi_table[0x36] = 0x00;
64749ab747fSPaolo Bonzini     pfl->cfi_table[0x37] = 0x00;
64849ab747fSPaolo Bonzini     pfl->cfi_table[0x38] = 0x00;
64949ab747fSPaolo Bonzini     pfl->cfi_table[0x39] = 0x00;
65049ab747fSPaolo Bonzini 
65149ab747fSPaolo Bonzini     pfl->cfi_table[0x3a] = 0x00;
65249ab747fSPaolo Bonzini 
65349ab747fSPaolo Bonzini     pfl->cfi_table[0x3b] = 0x00;
65449ab747fSPaolo Bonzini     pfl->cfi_table[0x3c] = 0x00;
65549ab747fSPaolo Bonzini }
65649ab747fSPaolo Bonzini 
65749ab747fSPaolo Bonzini static Property pflash_cfi02_properties[] = {
65816434065SMarkus Armbruster     DEFINE_PROP_DRIVE("drive", PFlashCFI02, blk),
65916434065SMarkus Armbruster     DEFINE_PROP_UINT32("num-blocks", PFlashCFI02, nb_blocs, 0),
66016434065SMarkus Armbruster     DEFINE_PROP_UINT32("sector-length", PFlashCFI02, sector_len, 0),
66116434065SMarkus Armbruster     DEFINE_PROP_UINT8("width", PFlashCFI02, width, 0),
66216434065SMarkus Armbruster     DEFINE_PROP_UINT8("mappings", PFlashCFI02, mappings, 0),
66316434065SMarkus Armbruster     DEFINE_PROP_UINT8("big-endian", PFlashCFI02, be, 0),
66416434065SMarkus Armbruster     DEFINE_PROP_UINT16("id0", PFlashCFI02, ident0, 0),
66516434065SMarkus Armbruster     DEFINE_PROP_UINT16("id1", PFlashCFI02, ident1, 0),
66616434065SMarkus Armbruster     DEFINE_PROP_UINT16("id2", PFlashCFI02, ident2, 0),
66716434065SMarkus Armbruster     DEFINE_PROP_UINT16("id3", PFlashCFI02, ident3, 0),
66816434065SMarkus Armbruster     DEFINE_PROP_UINT16("unlock-addr0", PFlashCFI02, unlock_addr0, 0),
66916434065SMarkus Armbruster     DEFINE_PROP_UINT16("unlock-addr1", PFlashCFI02, unlock_addr1, 0),
67016434065SMarkus Armbruster     DEFINE_PROP_STRING("name", PFlashCFI02, name),
67149ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
67249ab747fSPaolo Bonzini };
67349ab747fSPaolo Bonzini 
674d80cf1ebSStephen Checkoway static void pflash_cfi02_unrealize(DeviceState *dev, Error **errp)
675d80cf1ebSStephen Checkoway {
676e7b62741SMarkus Armbruster     PFlashCFI02 *pfl = PFLASH_CFI02(dev);
677d80cf1ebSStephen Checkoway     timer_del(&pfl->timer);
678d80cf1ebSStephen Checkoway }
679d80cf1ebSStephen Checkoway 
68049ab747fSPaolo Bonzini static void pflash_cfi02_class_init(ObjectClass *klass, void *data)
68149ab747fSPaolo Bonzini {
68249ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
68349ab747fSPaolo Bonzini 
684da3bd642SHu Tao     dc->realize = pflash_cfi02_realize;
685d80cf1ebSStephen Checkoway     dc->unrealize = pflash_cfi02_unrealize;
68649ab747fSPaolo Bonzini     dc->props = pflash_cfi02_properties;
687df6f9318SAntony Pavlov     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
68849ab747fSPaolo Bonzini }
68949ab747fSPaolo Bonzini 
69049ab747fSPaolo Bonzini static const TypeInfo pflash_cfi02_info = {
691e7b62741SMarkus Armbruster     .name           = TYPE_PFLASH_CFI02,
69249ab747fSPaolo Bonzini     .parent         = TYPE_SYS_BUS_DEVICE,
69316434065SMarkus Armbruster     .instance_size  = sizeof(PFlashCFI02),
69449ab747fSPaolo Bonzini     .class_init     = pflash_cfi02_class_init,
69549ab747fSPaolo Bonzini };
69649ab747fSPaolo Bonzini 
69749ab747fSPaolo Bonzini static void pflash_cfi02_register_types(void)
69849ab747fSPaolo Bonzini {
69949ab747fSPaolo Bonzini     type_register_static(&pflash_cfi02_info);
70049ab747fSPaolo Bonzini }
70149ab747fSPaolo Bonzini 
70249ab747fSPaolo Bonzini type_init(pflash_cfi02_register_types)
70349ab747fSPaolo Bonzini 
70416434065SMarkus Armbruster PFlashCFI02 *pflash_cfi02_register(hwaddr base,
705940d5b13SMarkus Armbruster                                    const char *name,
70649ab747fSPaolo Bonzini                                    hwaddr size,
70716434065SMarkus Armbruster                                    BlockBackend *blk,
708ce14710fSMarkus Armbruster                                    uint32_t sector_len,
70916434065SMarkus Armbruster                                    int nb_mappings, int width,
71049ab747fSPaolo Bonzini                                    uint16_t id0, uint16_t id1,
71149ab747fSPaolo Bonzini                                    uint16_t id2, uint16_t id3,
71216434065SMarkus Armbruster                                    uint16_t unlock_addr0,
71316434065SMarkus Armbruster                                    uint16_t unlock_addr1,
71449ab747fSPaolo Bonzini                                    int be)
71549ab747fSPaolo Bonzini {
716e7b62741SMarkus Armbruster     DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI02);
71749ab747fSPaolo Bonzini 
7189b3d111aSMarkus Armbruster     if (blk) {
7199b3d111aSMarkus Armbruster         qdev_prop_set_drive(dev, "drive", blk, &error_abort);
72049ab747fSPaolo Bonzini     }
721ce14710fSMarkus Armbruster     assert(size % sector_len == 0);
722ce14710fSMarkus Armbruster     qdev_prop_set_uint32(dev, "num-blocks", size / sector_len);
72349ab747fSPaolo Bonzini     qdev_prop_set_uint32(dev, "sector-length", sector_len);
72449ab747fSPaolo Bonzini     qdev_prop_set_uint8(dev, "width", width);
72549ab747fSPaolo Bonzini     qdev_prop_set_uint8(dev, "mappings", nb_mappings);
72649ab747fSPaolo Bonzini     qdev_prop_set_uint8(dev, "big-endian", !!be);
72749ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id0", id0);
72849ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id1", id1);
72949ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id2", id2);
73049ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id3", id3);
73149ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "unlock-addr0", unlock_addr0);
73249ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "unlock-addr1", unlock_addr1);
73349ab747fSPaolo Bonzini     qdev_prop_set_string(dev, "name", name);
73449ab747fSPaolo Bonzini     qdev_init_nofail(dev);
73549ab747fSPaolo Bonzini 
7363509c396SHu Tao     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
737e7b62741SMarkus Armbruster     return PFLASH_CFI02(dev);
73849ab747fSPaolo Bonzini }
739