xref: /qemu/hw/block/pflash_cfi02.c (revision 8a508e70)
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 software data protection as found in many real chips
3349ab747fSPaolo Bonzini  * It does not implement erase suspend/resume commands
3449ab747fSPaolo Bonzini  * It does not implement multiple sectors erase
3549ab747fSPaolo Bonzini  */
3649ab747fSPaolo Bonzini 
3780c71a24SPeter Maydell #include "qemu/osdep.h"
3849ab747fSPaolo Bonzini #include "hw/hw.h"
3906f15217SMarkus Armbruster #include "hw/block/block.h"
4049ab747fSPaolo Bonzini #include "hw/block/flash.h"
41da34e65cSMarkus Armbruster #include "qapi/error.h"
4249ab747fSPaolo Bonzini #include "qemu/timer.h"
434be74634SMarkus Armbruster #include "sysemu/block-backend.h"
4449ab747fSPaolo Bonzini #include "qemu/host-utils.h"
450b8fa32fSMarkus Armbruster #include "qemu/module.h"
4649ab747fSPaolo Bonzini #include "hw/sysbus.h"
4713019f1fSPhilippe Mathieu-Daudé #include "trace.h"
4849ab747fSPaolo Bonzini 
496536987fSPhilippe Mathieu-Daudé #define PFLASH_DEBUG false
5049ab747fSPaolo Bonzini #define DPRINTF(fmt, ...)                                  \
5149ab747fSPaolo Bonzini do {                                                       \
526536987fSPhilippe Mathieu-Daudé     if (PFLASH_DEBUG) {                                    \
5356f99ea1SAntony Pavlov         fprintf(stderr, "PFLASH: " fmt, ## __VA_ARGS__);   \
546536987fSPhilippe Mathieu-Daudé     }                                                      \
5549ab747fSPaolo Bonzini } while (0)
5649ab747fSPaolo Bonzini 
5749ab747fSPaolo Bonzini #define PFLASH_LAZY_ROMD_THRESHOLD 42
5849ab747fSPaolo Bonzini 
5964659053SStephen Checkoway /*
6064659053SStephen Checkoway  * The size of the cfi_table indirectly depends on this and the start of the
6164659053SStephen Checkoway  * PRI table directly depends on it. 4 is the maximum size (and also what
6264659053SStephen Checkoway  * seems common) without changing the PRT table address.
6364659053SStephen Checkoway  */
6464659053SStephen Checkoway #define PFLASH_MAX_ERASE_REGIONS 4
6564659053SStephen Checkoway 
66aeaf6c20SPhilippe Mathieu-Daudé /* Special write cycles for CFI queries. */
67aeaf6c20SPhilippe Mathieu-Daudé enum {
68aeaf6c20SPhilippe Mathieu-Daudé     WCYCLE_CFI              = 7,
69aeaf6c20SPhilippe Mathieu-Daudé };
70aeaf6c20SPhilippe Mathieu-Daudé 
7116434065SMarkus Armbruster struct PFlashCFI02 {
723509c396SHu Tao     /*< private >*/
733509c396SHu Tao     SysBusDevice parent_obj;
743509c396SHu Tao     /*< public >*/
753509c396SHu Tao 
764be74634SMarkus Armbruster     BlockBackend *blk;
7764659053SStephen Checkoway     uint32_t uniform_nb_blocs;
7864659053SStephen Checkoway     uint32_t uniform_sector_len;
7964659053SStephen Checkoway     uint32_t nb_blocs[PFLASH_MAX_ERASE_REGIONS];
8064659053SStephen Checkoway     uint32_t sector_len[PFLASH_MAX_ERASE_REGIONS];
8149ab747fSPaolo Bonzini     uint32_t chip_len;
8249ab747fSPaolo Bonzini     uint8_t mappings;
8349ab747fSPaolo Bonzini     uint8_t width;
8449ab747fSPaolo Bonzini     uint8_t be;
8549ab747fSPaolo Bonzini     int wcycle; /* if 0, the flash is read normally */
8649ab747fSPaolo Bonzini     int bypass;
8749ab747fSPaolo Bonzini     int ro;
8849ab747fSPaolo Bonzini     uint8_t cmd;
8949ab747fSPaolo Bonzini     uint8_t status;
9049ab747fSPaolo Bonzini     /* FIXME: implement array device properties */
9149ab747fSPaolo Bonzini     uint16_t ident0;
9249ab747fSPaolo Bonzini     uint16_t ident1;
9349ab747fSPaolo Bonzini     uint16_t ident2;
9449ab747fSPaolo Bonzini     uint16_t ident3;
9549ab747fSPaolo Bonzini     uint16_t unlock_addr0;
9649ab747fSPaolo Bonzini     uint16_t unlock_addr1;
9764659053SStephen Checkoway     uint8_t cfi_table[0x4d];
98d80cf1ebSStephen Checkoway     QEMUTimer timer;
9949ab747fSPaolo Bonzini     /* The device replicates the flash memory across its memory space.  Emulate
10049ab747fSPaolo Bonzini      * that by having a container (.mem) filled with an array of aliases
10149ab747fSPaolo Bonzini      * (.mem_mappings) pointing to the flash memory (.orig_mem).
10249ab747fSPaolo Bonzini      */
10349ab747fSPaolo Bonzini     MemoryRegion mem;
10449ab747fSPaolo Bonzini     MemoryRegion *mem_mappings;    /* array; one per mapping */
10549ab747fSPaolo Bonzini     MemoryRegion orig_mem;
10649ab747fSPaolo Bonzini     int rom_mode;
10749ab747fSPaolo Bonzini     int read_counter; /* used for lazy switch-back to rom mode */
10849ab747fSPaolo Bonzini     char *name;
10949ab747fSPaolo Bonzini     void *storage;
11049ab747fSPaolo Bonzini };
11149ab747fSPaolo Bonzini 
11249ab747fSPaolo Bonzini /*
1131d311e73SPhilippe Mathieu-Daudé  * Toggle status bit DQ7.
1141d311e73SPhilippe Mathieu-Daudé  */
1151d311e73SPhilippe Mathieu-Daudé static inline void toggle_dq7(PFlashCFI02 *pfl)
1161d311e73SPhilippe Mathieu-Daudé {
1171d311e73SPhilippe Mathieu-Daudé     pfl->status ^= 0x80;
1181d311e73SPhilippe Mathieu-Daudé }
1191d311e73SPhilippe Mathieu-Daudé 
1201d311e73SPhilippe Mathieu-Daudé /*
1211d311e73SPhilippe Mathieu-Daudé  * Set status bit DQ7 to bit 7 of value.
1221d311e73SPhilippe Mathieu-Daudé  */
1231d311e73SPhilippe Mathieu-Daudé static inline void set_dq7(PFlashCFI02 *pfl, uint8_t value)
1241d311e73SPhilippe Mathieu-Daudé {
1251d311e73SPhilippe Mathieu-Daudé     pfl->status &= 0x7F;
1261d311e73SPhilippe Mathieu-Daudé     pfl->status |= value & 0x80;
1271d311e73SPhilippe Mathieu-Daudé }
1281d311e73SPhilippe Mathieu-Daudé 
1291d311e73SPhilippe Mathieu-Daudé /*
1301d311e73SPhilippe Mathieu-Daudé  * Toggle status bit DQ6.
1311d311e73SPhilippe Mathieu-Daudé  */
1321d311e73SPhilippe Mathieu-Daudé static inline void toggle_dq6(PFlashCFI02 *pfl)
1331d311e73SPhilippe Mathieu-Daudé {
1341d311e73SPhilippe Mathieu-Daudé     pfl->status ^= 0x40;
1351d311e73SPhilippe Mathieu-Daudé }
1361d311e73SPhilippe Mathieu-Daudé 
1371d311e73SPhilippe Mathieu-Daudé /*
13849ab747fSPaolo Bonzini  * Set up replicated mappings of the same region.
13949ab747fSPaolo Bonzini  */
14016434065SMarkus Armbruster static void pflash_setup_mappings(PFlashCFI02 *pfl)
14149ab747fSPaolo Bonzini {
14249ab747fSPaolo Bonzini     unsigned i;
14349ab747fSPaolo Bonzini     hwaddr size = memory_region_size(&pfl->orig_mem);
14449ab747fSPaolo Bonzini 
1452d256e6fSPaolo Bonzini     memory_region_init(&pfl->mem, OBJECT(pfl), "pflash", pfl->mappings * size);
14649ab747fSPaolo Bonzini     pfl->mem_mappings = g_new(MemoryRegion, pfl->mappings);
14749ab747fSPaolo Bonzini     for (i = 0; i < pfl->mappings; ++i) {
1482d256e6fSPaolo Bonzini         memory_region_init_alias(&pfl->mem_mappings[i], OBJECT(pfl),
1492d256e6fSPaolo Bonzini                                  "pflash-alias", &pfl->orig_mem, 0, size);
15049ab747fSPaolo Bonzini         memory_region_add_subregion(&pfl->mem, i * size, &pfl->mem_mappings[i]);
15149ab747fSPaolo Bonzini     }
15249ab747fSPaolo Bonzini }
15349ab747fSPaolo Bonzini 
15416434065SMarkus Armbruster static void pflash_register_memory(PFlashCFI02 *pfl, int rom_mode)
15549ab747fSPaolo Bonzini {
1565f9a5ea1SJan Kiszka     memory_region_rom_device_set_romd(&pfl->orig_mem, rom_mode);
15749ab747fSPaolo Bonzini     pfl->rom_mode = rom_mode;
15849ab747fSPaolo Bonzini }
15949ab747fSPaolo Bonzini 
160102f0f79SPhilippe Mathieu-Daudé static size_t pflash_regions_count(PFlashCFI02 *pfl)
161102f0f79SPhilippe Mathieu-Daudé {
162102f0f79SPhilippe Mathieu-Daudé     return pfl->cfi_table[0x2c];
163102f0f79SPhilippe Mathieu-Daudé }
164102f0f79SPhilippe Mathieu-Daudé 
16549ab747fSPaolo Bonzini static void pflash_timer (void *opaque)
16649ab747fSPaolo Bonzini {
16716434065SMarkus Armbruster     PFlashCFI02 *pfl = opaque;
16849ab747fSPaolo Bonzini 
16913019f1fSPhilippe Mathieu-Daudé     trace_pflash_timer_expired(pfl->cmd);
17049ab747fSPaolo Bonzini     /* Reset flash */
1711d311e73SPhilippe Mathieu-Daudé     toggle_dq7(pfl);
17249ab747fSPaolo Bonzini     if (pfl->bypass) {
17349ab747fSPaolo Bonzini         pfl->wcycle = 2;
17449ab747fSPaolo Bonzini     } else {
17549ab747fSPaolo Bonzini         pflash_register_memory(pfl, 1);
17649ab747fSPaolo Bonzini         pfl->wcycle = 0;
17749ab747fSPaolo Bonzini     }
17849ab747fSPaolo Bonzini     pfl->cmd = 0;
17949ab747fSPaolo Bonzini }
18049ab747fSPaolo Bonzini 
18106e8b8e3SPhilippe Mathieu-Daudé /*
18206e8b8e3SPhilippe Mathieu-Daudé  * Read data from flash.
18306e8b8e3SPhilippe Mathieu-Daudé  */
18406e8b8e3SPhilippe Mathieu-Daudé static uint64_t pflash_data_read(PFlashCFI02 *pfl, hwaddr offset,
18506e8b8e3SPhilippe Mathieu-Daudé                                  unsigned int width)
18606e8b8e3SPhilippe Mathieu-Daudé {
18706e8b8e3SPhilippe Mathieu-Daudé     uint8_t *p = (uint8_t *)pfl->storage + offset;
18806e8b8e3SPhilippe Mathieu-Daudé     uint64_t ret = pfl->be ? ldn_be_p(p, width) : ldn_le_p(p, width);
18906e8b8e3SPhilippe Mathieu-Daudé     trace_pflash_data_read(offset, width << 1, ret);
19006e8b8e3SPhilippe Mathieu-Daudé     return ret;
19106e8b8e3SPhilippe Mathieu-Daudé }
19206e8b8e3SPhilippe Mathieu-Daudé 
19364659053SStephen Checkoway /*
19464659053SStephen Checkoway  * offset should be a byte offset of the QEMU device and _not_ a device
19564659053SStephen Checkoway  * offset.
19664659053SStephen Checkoway  */
19764659053SStephen Checkoway static uint32_t pflash_sector_len(PFlashCFI02 *pfl, hwaddr offset)
19864659053SStephen Checkoway {
19964659053SStephen Checkoway     assert(offset < pfl->chip_len);
20064659053SStephen Checkoway     hwaddr addr = 0;
201102f0f79SPhilippe Mathieu-Daudé     for (int i = 0; i < pflash_regions_count(pfl); ++i) {
20264659053SStephen Checkoway         uint64_t region_size = (uint64_t)pfl->nb_blocs[i] * pfl->sector_len[i];
20364659053SStephen Checkoway         if (addr <= offset && offset < addr + region_size) {
20464659053SStephen Checkoway             return pfl->sector_len[i];
20564659053SStephen Checkoway         }
20664659053SStephen Checkoway         addr += region_size;
20764659053SStephen Checkoway     }
20864659053SStephen Checkoway     abort();
20964659053SStephen Checkoway }
21064659053SStephen Checkoway 
211aff498cfSPhilippe Mathieu-Daudé static uint64_t pflash_read(void *opaque, hwaddr offset, unsigned int width)
21249ab747fSPaolo Bonzini {
213aff498cfSPhilippe Mathieu-Daudé     PFlashCFI02 *pfl = opaque;
21449ab747fSPaolo Bonzini     hwaddr boff;
215aff498cfSPhilippe Mathieu-Daudé     uint64_t ret;
21649ab747fSPaolo Bonzini 
21749ab747fSPaolo Bonzini     ret = -1;
21849ab747fSPaolo Bonzini     /* Lazy reset to ROMD mode after a certain amount of read accesses */
21949ab747fSPaolo Bonzini     if (!pfl->rom_mode && pfl->wcycle == 0 &&
22049ab747fSPaolo Bonzini         ++pfl->read_counter > PFLASH_LAZY_ROMD_THRESHOLD) {
22149ab747fSPaolo Bonzini         pflash_register_memory(pfl, 1);
22249ab747fSPaolo Bonzini     }
22349ab747fSPaolo Bonzini     offset &= pfl->chip_len - 1;
22449ab747fSPaolo Bonzini     boff = offset & 0xFF;
22564659053SStephen Checkoway     if (pfl->width == 2) {
22649ab747fSPaolo Bonzini         boff = boff >> 1;
22764659053SStephen Checkoway     } else if (pfl->width == 4) {
22849ab747fSPaolo Bonzini         boff = boff >> 2;
22964659053SStephen Checkoway     }
23049ab747fSPaolo Bonzini     switch (pfl->cmd) {
23149ab747fSPaolo Bonzini     default:
23249ab747fSPaolo Bonzini         /* This should never happen : reset state & treat it as a read*/
23349ab747fSPaolo Bonzini         DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd);
23449ab747fSPaolo Bonzini         pfl->wcycle = 0;
23549ab747fSPaolo Bonzini         pfl->cmd = 0;
23649ab747fSPaolo Bonzini         /* fall through to the read code */
23749ab747fSPaolo Bonzini     case 0x80:
23849ab747fSPaolo Bonzini         /* We accept reads during second unlock sequence... */
23949ab747fSPaolo Bonzini     case 0x00:
24049ab747fSPaolo Bonzini         /* Flash area read */
24106e8b8e3SPhilippe Mathieu-Daudé         ret = pflash_data_read(pfl, offset, width);
24249ab747fSPaolo Bonzini         break;
24349ab747fSPaolo Bonzini     case 0x90:
24449ab747fSPaolo Bonzini         /* flash ID read */
24549ab747fSPaolo Bonzini         switch (boff) {
24649ab747fSPaolo Bonzini         case 0x00:
24749ab747fSPaolo Bonzini         case 0x01:
24849ab747fSPaolo Bonzini             ret = boff & 0x01 ? pfl->ident1 : pfl->ident0;
24949ab747fSPaolo Bonzini             break;
25049ab747fSPaolo Bonzini         case 0x02:
25149ab747fSPaolo Bonzini             ret = 0x00; /* Pretend all sectors are unprotected */
25249ab747fSPaolo Bonzini             break;
25349ab747fSPaolo Bonzini         case 0x0E:
25449ab747fSPaolo Bonzini         case 0x0F:
25549ab747fSPaolo Bonzini             ret = boff & 0x01 ? pfl->ident3 : pfl->ident2;
2567f7bdcafSPhilippe Mathieu-Daudé             if (ret != (uint8_t)-1) {
25749ab747fSPaolo Bonzini                 break;
2587f7bdcafSPhilippe Mathieu-Daudé             }
2597f7bdcafSPhilippe Mathieu-Daudé             /* Fall through to data read. */
26049ab747fSPaolo Bonzini         default:
26106e8b8e3SPhilippe Mathieu-Daudé             ret = pflash_data_read(pfl, offset, width);
26249ab747fSPaolo Bonzini         }
263aff498cfSPhilippe Mathieu-Daudé         DPRINTF("%s: ID " TARGET_FMT_plx " %" PRIx64 "\n", __func__, boff, ret);
26449ab747fSPaolo Bonzini         break;
26549ab747fSPaolo Bonzini     case 0xA0:
26649ab747fSPaolo Bonzini     case 0x10:
26749ab747fSPaolo Bonzini     case 0x30:
26849ab747fSPaolo Bonzini         /* Status register read */
26949ab747fSPaolo Bonzini         ret = pfl->status;
270aff498cfSPhilippe Mathieu-Daudé         DPRINTF("%s: status %" PRIx64 "\n", __func__, ret);
2711d311e73SPhilippe Mathieu-Daudé         toggle_dq6(pfl);
27249ab747fSPaolo Bonzini         break;
27349ab747fSPaolo Bonzini     case 0x98:
27449ab747fSPaolo Bonzini         /* CFI query mode */
27507c13a71SPhilippe Mathieu-Daudé         if (boff < sizeof(pfl->cfi_table)) {
27649ab747fSPaolo Bonzini             ret = pfl->cfi_table[boff];
27707c13a71SPhilippe Mathieu-Daudé         } else {
27807c13a71SPhilippe Mathieu-Daudé             ret = 0;
27907c13a71SPhilippe Mathieu-Daudé         }
28049ab747fSPaolo Bonzini         break;
28149ab747fSPaolo Bonzini     }
282e8aa2d95SPhilippe Mathieu-Daudé     trace_pflash_io_read(offset, width, width << 1, ret, pfl->cmd, pfl->wcycle);
28349ab747fSPaolo Bonzini 
28449ab747fSPaolo Bonzini     return ret;
28549ab747fSPaolo Bonzini }
28649ab747fSPaolo Bonzini 
28749ab747fSPaolo Bonzini /* update flash content on disk */
288aff498cfSPhilippe Mathieu-Daudé static void pflash_update(PFlashCFI02 *pfl, int offset, int size)
28949ab747fSPaolo Bonzini {
29049ab747fSPaolo Bonzini     int offset_end;
2914be74634SMarkus Armbruster     if (pfl->blk) {
29249ab747fSPaolo Bonzini         offset_end = offset + size;
293098e732dSEric Blake         /* widen to sector boundaries */
294098e732dSEric Blake         offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE);
295098e732dSEric Blake         offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE);
296098e732dSEric Blake         blk_pwrite(pfl->blk, offset, pfl->storage + offset,
297098e732dSEric Blake                    offset_end - offset, 0);
29849ab747fSPaolo Bonzini     }
29949ab747fSPaolo Bonzini }
30049ab747fSPaolo Bonzini 
301aff498cfSPhilippe Mathieu-Daudé static void pflash_write(void *opaque, hwaddr offset, uint64_t value,
302aff498cfSPhilippe Mathieu-Daudé                          unsigned int width)
30349ab747fSPaolo Bonzini {
304aff498cfSPhilippe Mathieu-Daudé     PFlashCFI02 *pfl = opaque;
30549ab747fSPaolo Bonzini     hwaddr boff;
30649ab747fSPaolo Bonzini     uint8_t *p;
30749ab747fSPaolo Bonzini     uint8_t cmd;
30864659053SStephen Checkoway     uint32_t sector_len;
30949ab747fSPaolo Bonzini 
310e8aa2d95SPhilippe Mathieu-Daudé     trace_pflash_io_write(offset, width, width << 1, value, pfl->wcycle);
31149ab747fSPaolo Bonzini     cmd = value;
312*8a508e70SPhilippe Mathieu-Daudé     if (pfl->cmd != 0xA0) {
313*8a508e70SPhilippe Mathieu-Daudé         if (cmd == 0xF0) {
31449ab747fSPaolo Bonzini             goto reset_flash;
31549ab747fSPaolo Bonzini         }
316*8a508e70SPhilippe Mathieu-Daudé     }
31749ab747fSPaolo Bonzini     offset &= pfl->chip_len - 1;
31849ab747fSPaolo Bonzini 
3196682bc1eSStephen Checkoway     boff = offset;
32064659053SStephen Checkoway     if (pfl->width == 2) {
32149ab747fSPaolo Bonzini         boff = boff >> 1;
32264659053SStephen Checkoway     } else if (pfl->width == 4) {
32349ab747fSPaolo Bonzini         boff = boff >> 2;
32464659053SStephen Checkoway     }
3256682bc1eSStephen Checkoway     /* Only the least-significant 11 bits are used in most cases. */
3266682bc1eSStephen Checkoway     boff &= 0x7FF;
32749ab747fSPaolo Bonzini     switch (pfl->wcycle) {
32849ab747fSPaolo Bonzini     case 0:
32949ab747fSPaolo Bonzini         /* Set the device in I/O access mode if required */
33049ab747fSPaolo Bonzini         if (pfl->rom_mode)
33149ab747fSPaolo Bonzini             pflash_register_memory(pfl, 0);
33249ab747fSPaolo Bonzini         pfl->read_counter = 0;
33349ab747fSPaolo Bonzini         /* We're in read mode */
33449ab747fSPaolo Bonzini     check_unlock0:
33549ab747fSPaolo Bonzini         if (boff == 0x55 && cmd == 0x98) {
33649ab747fSPaolo Bonzini         enter_CFI_mode:
33749ab747fSPaolo Bonzini             /* Enter CFI query mode */
338aeaf6c20SPhilippe Mathieu-Daudé             pfl->wcycle = WCYCLE_CFI;
33949ab747fSPaolo Bonzini             pfl->cmd = 0x98;
34049ab747fSPaolo Bonzini             return;
34149ab747fSPaolo Bonzini         }
34249ab747fSPaolo Bonzini         if (boff != pfl->unlock_addr0 || cmd != 0xAA) {
34349ab747fSPaolo Bonzini             DPRINTF("%s: unlock0 failed " TARGET_FMT_plx " %02x %04x\n",
34449ab747fSPaolo Bonzini                     __func__, boff, cmd, pfl->unlock_addr0);
34549ab747fSPaolo Bonzini             goto reset_flash;
34649ab747fSPaolo Bonzini         }
34749ab747fSPaolo Bonzini         DPRINTF("%s: unlock sequence started\n", __func__);
34849ab747fSPaolo Bonzini         break;
34949ab747fSPaolo Bonzini     case 1:
35049ab747fSPaolo Bonzini         /* We started an unlock sequence */
35149ab747fSPaolo Bonzini     check_unlock1:
35249ab747fSPaolo Bonzini         if (boff != pfl->unlock_addr1 || cmd != 0x55) {
35349ab747fSPaolo Bonzini             DPRINTF("%s: unlock1 failed " TARGET_FMT_plx " %02x\n", __func__,
35449ab747fSPaolo Bonzini                     boff, cmd);
35549ab747fSPaolo Bonzini             goto reset_flash;
35649ab747fSPaolo Bonzini         }
35749ab747fSPaolo Bonzini         DPRINTF("%s: unlock sequence done\n", __func__);
35849ab747fSPaolo Bonzini         break;
35949ab747fSPaolo Bonzini     case 2:
36049ab747fSPaolo Bonzini         /* We finished an unlock sequence */
36149ab747fSPaolo Bonzini         if (!pfl->bypass && boff != pfl->unlock_addr0) {
36249ab747fSPaolo Bonzini             DPRINTF("%s: command failed " TARGET_FMT_plx " %02x\n", __func__,
36349ab747fSPaolo Bonzini                     boff, cmd);
36449ab747fSPaolo Bonzini             goto reset_flash;
36549ab747fSPaolo Bonzini         }
36649ab747fSPaolo Bonzini         switch (cmd) {
36749ab747fSPaolo Bonzini         case 0x20:
36849ab747fSPaolo Bonzini             pfl->bypass = 1;
36949ab747fSPaolo Bonzini             goto do_bypass;
37049ab747fSPaolo Bonzini         case 0x80:
37149ab747fSPaolo Bonzini         case 0x90:
37249ab747fSPaolo Bonzini         case 0xA0:
37349ab747fSPaolo Bonzini             pfl->cmd = cmd;
37449ab747fSPaolo Bonzini             DPRINTF("%s: starting command %02x\n", __func__, cmd);
37549ab747fSPaolo Bonzini             break;
37649ab747fSPaolo Bonzini         default:
37749ab747fSPaolo Bonzini             DPRINTF("%s: unknown command %02x\n", __func__, cmd);
37849ab747fSPaolo Bonzini             goto reset_flash;
37949ab747fSPaolo Bonzini         }
38049ab747fSPaolo Bonzini         break;
38149ab747fSPaolo Bonzini     case 3:
38249ab747fSPaolo Bonzini         switch (pfl->cmd) {
38349ab747fSPaolo Bonzini         case 0x80:
38449ab747fSPaolo Bonzini             /* We need another unlock sequence */
38549ab747fSPaolo Bonzini             goto check_unlock0;
38649ab747fSPaolo Bonzini         case 0xA0:
387c1474acdSPhilippe Mathieu-Daudé             trace_pflash_data_write(offset, width << 1, value, 0);
38849ab747fSPaolo Bonzini             if (!pfl->ro) {
389c3d25271SPhilippe Mathieu-Daudé                 p = (uint8_t *)pfl->storage + offset;
390c3d25271SPhilippe Mathieu-Daudé                 if (pfl->be) {
391c3d25271SPhilippe Mathieu-Daudé                     uint64_t current = ldn_be_p(p, width);
392c3d25271SPhilippe Mathieu-Daudé                     stn_be_p(p, width, current & value);
39349ab747fSPaolo Bonzini                 } else {
394c3d25271SPhilippe Mathieu-Daudé                     uint64_t current = ldn_le_p(p, width);
395c3d25271SPhilippe Mathieu-Daudé                     stn_le_p(p, width, current & value);
39649ab747fSPaolo Bonzini                 }
397c3d25271SPhilippe Mathieu-Daudé                 pflash_update(pfl, offset, width);
39849ab747fSPaolo Bonzini             }
3991d311e73SPhilippe Mathieu-Daudé             /*
4001d311e73SPhilippe Mathieu-Daudé              * While programming, status bit DQ7 should hold the opposite
4011d311e73SPhilippe Mathieu-Daudé              * value from how it was programmed.
4021d311e73SPhilippe Mathieu-Daudé              */
4031d311e73SPhilippe Mathieu-Daudé             set_dq7(pfl, ~value);
40449ab747fSPaolo Bonzini             /* Let's pretend write is immediate */
40549ab747fSPaolo Bonzini             if (pfl->bypass)
40649ab747fSPaolo Bonzini                 goto do_bypass;
40749ab747fSPaolo Bonzini             goto reset_flash;
40849ab747fSPaolo Bonzini         case 0x90:
40949ab747fSPaolo Bonzini             if (pfl->bypass && cmd == 0x00) {
41049ab747fSPaolo Bonzini                 /* Unlock bypass reset */
41149ab747fSPaolo Bonzini                 goto reset_flash;
41249ab747fSPaolo Bonzini             }
41349ab747fSPaolo Bonzini             /* We can enter CFI query mode from autoselect mode */
41449ab747fSPaolo Bonzini             if (boff == 0x55 && cmd == 0x98)
41549ab747fSPaolo Bonzini                 goto enter_CFI_mode;
41649ab747fSPaolo Bonzini             /* No break here */
41749ab747fSPaolo Bonzini         default:
41849ab747fSPaolo Bonzini             DPRINTF("%s: invalid write for command %02x\n",
41949ab747fSPaolo Bonzini                     __func__, pfl->cmd);
42049ab747fSPaolo Bonzini             goto reset_flash;
42149ab747fSPaolo Bonzini         }
42249ab747fSPaolo Bonzini     case 4:
42349ab747fSPaolo Bonzini         switch (pfl->cmd) {
42449ab747fSPaolo Bonzini         case 0xA0:
42549ab747fSPaolo Bonzini             /* Ignore writes while flash data write is occurring */
42649ab747fSPaolo Bonzini             /* As we suppose write is immediate, this should never happen */
42749ab747fSPaolo Bonzini             return;
42849ab747fSPaolo Bonzini         case 0x80:
42949ab747fSPaolo Bonzini             goto check_unlock1;
43049ab747fSPaolo Bonzini         default:
43149ab747fSPaolo Bonzini             /* Should never happen */
43249ab747fSPaolo Bonzini             DPRINTF("%s: invalid command state %02x (wc 4)\n",
43349ab747fSPaolo Bonzini                     __func__, pfl->cmd);
43449ab747fSPaolo Bonzini             goto reset_flash;
43549ab747fSPaolo Bonzini         }
43649ab747fSPaolo Bonzini         break;
43749ab747fSPaolo Bonzini     case 5:
43849ab747fSPaolo Bonzini         switch (cmd) {
43949ab747fSPaolo Bonzini         case 0x10:
44049ab747fSPaolo Bonzini             if (boff != pfl->unlock_addr0) {
44149ab747fSPaolo Bonzini                 DPRINTF("%s: chip erase: invalid address " TARGET_FMT_plx "\n",
44249ab747fSPaolo Bonzini                         __func__, offset);
44349ab747fSPaolo Bonzini                 goto reset_flash;
44449ab747fSPaolo Bonzini             }
44549ab747fSPaolo Bonzini             /* Chip erase */
44649ab747fSPaolo Bonzini             DPRINTF("%s: start chip erase\n", __func__);
44749ab747fSPaolo Bonzini             if (!pfl->ro) {
4481eb27d69SPhilippe Mathieu-Daudé                 memset(pfl->storage, 0xff, pfl->chip_len);
44949ab747fSPaolo Bonzini                 pflash_update(pfl, 0, pfl->chip_len);
45049ab747fSPaolo Bonzini             }
4511d311e73SPhilippe Mathieu-Daudé             set_dq7(pfl, 0x00);
45249ab747fSPaolo Bonzini             /* Let's wait 5 seconds before chip erase is done */
453d80cf1ebSStephen Checkoway             timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
45473bcb24dSRutuja Shah                       (NANOSECONDS_PER_SECOND * 5));
45549ab747fSPaolo Bonzini             break;
45649ab747fSPaolo Bonzini         case 0x30:
45749ab747fSPaolo Bonzini             /* Sector erase */
45849ab747fSPaolo Bonzini             p = pfl->storage;
45964659053SStephen Checkoway             sector_len = pflash_sector_len(pfl, offset);
46064659053SStephen Checkoway             offset &= ~(sector_len - 1);
46164659053SStephen Checkoway             DPRINTF("%s: start sector erase at %0*" PRIx64 "-%0*" PRIx64 "\n",
46264659053SStephen Checkoway                     __func__, pfl->width * 2, offset,
46364659053SStephen Checkoway                     pfl->width * 2, offset + sector_len - 1);
46449ab747fSPaolo Bonzini             if (!pfl->ro) {
46564659053SStephen Checkoway                 memset(p + offset, 0xff, sector_len);
46664659053SStephen Checkoway                 pflash_update(pfl, offset, 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 
520aff498cfSPhilippe Mathieu-Daudé static const MemoryRegionOps pflash_cfi02_ops = {
521aff498cfSPhilippe Mathieu-Daudé     .read = pflash_read,
522aff498cfSPhilippe Mathieu-Daudé     .write = pflash_write,
523a4afb28dSPeter Maydell     .valid.min_access_size = 1,
524a4afb28dSPeter Maydell     .valid.max_access_size = 4,
52549ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
52649ab747fSPaolo Bonzini };
52749ab747fSPaolo Bonzini 
528da3bd642SHu Tao static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
52949ab747fSPaolo Bonzini {
530e7b62741SMarkus Armbruster     PFlashCFI02 *pfl = PFLASH_CFI02(dev);
53149ab747fSPaolo Bonzini     int ret;
53233e0eb52SHu Tao     Error *local_err = NULL;
53349ab747fSPaolo Bonzini 
53464659053SStephen Checkoway     if (pfl->uniform_sector_len == 0 && pfl->sector_len[0] == 0) {
5358929fc3aSZiyue Yang         error_setg(errp, "attribute \"sector-length\" not specified or zero.");
5368929fc3aSZiyue Yang         return;
5378929fc3aSZiyue Yang     }
53864659053SStephen Checkoway     if (pfl->uniform_nb_blocs == 0 && pfl->nb_blocs[0] == 0) {
5398929fc3aSZiyue Yang         error_setg(errp, "attribute \"num-blocks\" not specified or zero.");
5408929fc3aSZiyue Yang         return;
5418929fc3aSZiyue Yang     }
5428929fc3aSZiyue Yang     if (pfl->name == NULL) {
5438929fc3aSZiyue Yang         error_setg(errp, "attribute \"name\" not specified.");
5448929fc3aSZiyue Yang         return;
5458929fc3aSZiyue Yang     }
5468929fc3aSZiyue Yang 
54764659053SStephen Checkoway     int nb_regions;
54864659053SStephen Checkoway     pfl->chip_len = 0;
54964659053SStephen Checkoway     for (nb_regions = 0; nb_regions < PFLASH_MAX_ERASE_REGIONS; ++nb_regions) {
55064659053SStephen Checkoway         if (pfl->nb_blocs[nb_regions] == 0) {
55164659053SStephen Checkoway             break;
55264659053SStephen Checkoway         }
55364659053SStephen Checkoway         uint64_t sector_len_per_device = pfl->sector_len[nb_regions];
55464659053SStephen Checkoway 
55564659053SStephen Checkoway         /*
55664659053SStephen Checkoway          * The size of each flash sector must be a power of 2 and it must be
55764659053SStephen Checkoway          * aligned at the same power of 2.
55864659053SStephen Checkoway          */
55964659053SStephen Checkoway         if (sector_len_per_device & 0xff ||
56064659053SStephen Checkoway             sector_len_per_device >= (1 << 24) ||
56164659053SStephen Checkoway             !is_power_of_2(sector_len_per_device))
56264659053SStephen Checkoway         {
56364659053SStephen Checkoway             error_setg(errp, "unsupported configuration: "
56464659053SStephen Checkoway                        "sector length[%d] per device = %" PRIx64 ".",
56564659053SStephen Checkoway                        nb_regions, sector_len_per_device);
56664659053SStephen Checkoway             return;
56764659053SStephen Checkoway         }
56864659053SStephen Checkoway         if (pfl->chip_len & (sector_len_per_device - 1)) {
56964659053SStephen Checkoway             error_setg(errp, "unsupported configuration: "
57064659053SStephen Checkoway                        "flash region %d not correctly aligned.",
57164659053SStephen Checkoway                        nb_regions);
57264659053SStephen Checkoway             return;
57364659053SStephen Checkoway         }
57464659053SStephen Checkoway 
57564659053SStephen Checkoway         pfl->chip_len += (uint64_t)pfl->sector_len[nb_regions] *
57664659053SStephen Checkoway                           pfl->nb_blocs[nb_regions];
57764659053SStephen Checkoway     }
57864659053SStephen Checkoway 
57964659053SStephen Checkoway     uint64_t uniform_len = (uint64_t)pfl->uniform_nb_blocs *
58064659053SStephen Checkoway                            pfl->uniform_sector_len;
58164659053SStephen Checkoway     if (nb_regions == 0) {
58264659053SStephen Checkoway         nb_regions = 1;
58364659053SStephen Checkoway         pfl->nb_blocs[0] = pfl->uniform_nb_blocs;
58464659053SStephen Checkoway         pfl->sector_len[0] = pfl->uniform_sector_len;
58564659053SStephen Checkoway         pfl->chip_len = uniform_len;
58664659053SStephen Checkoway     } else if (uniform_len != 0 && uniform_len != pfl->chip_len) {
58764659053SStephen Checkoway         error_setg(errp, "\"num-blocks\"*\"sector-length\" "
58864659053SStephen Checkoway                    "different from \"num-blocks0\"*\'sector-length0\" + ... + "
58964659053SStephen Checkoway                    "\"num-blocks3\"*\"sector-length3\"");
59064659053SStephen Checkoway         return;
59164659053SStephen Checkoway     }
59249ab747fSPaolo Bonzini 
593aff498cfSPhilippe Mathieu-Daudé     memory_region_init_rom_device(&pfl->orig_mem, OBJECT(pfl),
594aff498cfSPhilippe Mathieu-Daudé                                   &pflash_cfi02_ops, pfl, pfl->name,
5951eb27d69SPhilippe Mathieu-Daudé                                   pfl->chip_len, &local_err);
59633e0eb52SHu Tao     if (local_err) {
59733e0eb52SHu Tao         error_propagate(errp, local_err);
59833e0eb52SHu Tao         return;
59933e0eb52SHu Tao     }
60033e0eb52SHu Tao 
60149ab747fSPaolo Bonzini     pfl->storage = memory_region_get_ram_ptr(&pfl->orig_mem);
602a17c17a2SKevin Wolf 
603a17c17a2SKevin Wolf     if (pfl->blk) {
604a17c17a2SKevin Wolf         uint64_t perm;
605a17c17a2SKevin Wolf         pfl->ro = blk_is_read_only(pfl->blk);
606a17c17a2SKevin Wolf         perm = BLK_PERM_CONSISTENT_READ | (pfl->ro ? 0 : BLK_PERM_WRITE);
607a17c17a2SKevin Wolf         ret = blk_set_perm(pfl->blk, perm, BLK_PERM_ALL, errp);
608a17c17a2SKevin Wolf         if (ret < 0) {
609a17c17a2SKevin Wolf             return;
610a17c17a2SKevin Wolf         }
611a17c17a2SKevin Wolf     } else {
612a17c17a2SKevin Wolf         pfl->ro = 0;
613a17c17a2SKevin Wolf     }
614a17c17a2SKevin Wolf 
6154be74634SMarkus Armbruster     if (pfl->blk) {
6161eb27d69SPhilippe Mathieu-Daudé         if (!blk_check_size_and_read_all(pfl->blk, pfl->storage,
6171eb27d69SPhilippe Mathieu-Daudé                                          pfl->chip_len, errp)) {
618da3bd642SHu Tao             vmstate_unregister_ram(&pfl->orig_mem, DEVICE(pfl));
619da3bd642SHu Tao             return;
62049ab747fSPaolo Bonzini         }
62149ab747fSPaolo Bonzini     }
62249ab747fSPaolo Bonzini 
6236682bc1eSStephen Checkoway     /* Only 11 bits are used in the comparison. */
6246682bc1eSStephen Checkoway     pfl->unlock_addr0 &= 0x7FF;
6256682bc1eSStephen Checkoway     pfl->unlock_addr1 &= 0x7FF;
6266682bc1eSStephen Checkoway 
62749ab747fSPaolo Bonzini     pflash_setup_mappings(pfl);
62849ab747fSPaolo Bonzini     pfl->rom_mode = 1;
629da3bd642SHu Tao     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem);
63049ab747fSPaolo Bonzini 
631d80cf1ebSStephen Checkoway     timer_init_ns(&pfl->timer, QEMU_CLOCK_VIRTUAL, pflash_timer, pfl);
63249ab747fSPaolo Bonzini     pfl->wcycle = 0;
63349ab747fSPaolo Bonzini     pfl->cmd = 0;
63449ab747fSPaolo Bonzini     pfl->status = 0;
6359ac45b88SPhilippe Mathieu-Daudé 
63649ab747fSPaolo Bonzini     /* Hardcoded CFI table (mostly from SG29 Spansion flash) */
63764659053SStephen Checkoway     const uint16_t pri_ofs = 0x40;
63849ab747fSPaolo Bonzini     /* Standard "QRY" string */
63949ab747fSPaolo Bonzini     pfl->cfi_table[0x10] = 'Q';
64049ab747fSPaolo Bonzini     pfl->cfi_table[0x11] = 'R';
64149ab747fSPaolo Bonzini     pfl->cfi_table[0x12] = 'Y';
64249ab747fSPaolo Bonzini     /* Command set (AMD/Fujitsu) */
64349ab747fSPaolo Bonzini     pfl->cfi_table[0x13] = 0x02;
64449ab747fSPaolo Bonzini     pfl->cfi_table[0x14] = 0x00;
64549ab747fSPaolo Bonzini     /* Primary extended table address */
646d6874c83SPhilippe Mathieu-Daudé     pfl->cfi_table[0x15] = pri_ofs;
647d6874c83SPhilippe Mathieu-Daudé     pfl->cfi_table[0x16] = pri_ofs >> 8;
64849ab747fSPaolo Bonzini     /* Alternate command set (none) */
64949ab747fSPaolo Bonzini     pfl->cfi_table[0x17] = 0x00;
65049ab747fSPaolo Bonzini     pfl->cfi_table[0x18] = 0x00;
65149ab747fSPaolo Bonzini     /* Alternate extended table (none) */
65249ab747fSPaolo Bonzini     pfl->cfi_table[0x19] = 0x00;
65349ab747fSPaolo Bonzini     pfl->cfi_table[0x1A] = 0x00;
65449ab747fSPaolo Bonzini     /* Vcc min */
65549ab747fSPaolo Bonzini     pfl->cfi_table[0x1B] = 0x27;
65649ab747fSPaolo Bonzini     /* Vcc max */
65749ab747fSPaolo Bonzini     pfl->cfi_table[0x1C] = 0x36;
65849ab747fSPaolo Bonzini     /* Vpp min (no Vpp pin) */
65949ab747fSPaolo Bonzini     pfl->cfi_table[0x1D] = 0x00;
66049ab747fSPaolo Bonzini     /* Vpp max (no Vpp pin) */
66149ab747fSPaolo Bonzini     pfl->cfi_table[0x1E] = 0x00;
6629ac45b88SPhilippe Mathieu-Daudé     /* Timeout per single byte/word write (128 ms) */
66349ab747fSPaolo Bonzini     pfl->cfi_table[0x1F] = 0x07;
66449ab747fSPaolo Bonzini     /* Timeout for min size buffer write (NA) */
66549ab747fSPaolo Bonzini     pfl->cfi_table[0x20] = 0x00;
66649ab747fSPaolo Bonzini     /* Typical timeout for block erase (512 ms) */
66749ab747fSPaolo Bonzini     pfl->cfi_table[0x21] = 0x09;
66849ab747fSPaolo Bonzini     /* Typical timeout for full chip erase (4096 ms) */
66949ab747fSPaolo Bonzini     pfl->cfi_table[0x22] = 0x0C;
67049ab747fSPaolo Bonzini     /* Reserved */
67149ab747fSPaolo Bonzini     pfl->cfi_table[0x23] = 0x01;
67249ab747fSPaolo Bonzini     /* Max timeout for buffer write (NA) */
67349ab747fSPaolo Bonzini     pfl->cfi_table[0x24] = 0x00;
67449ab747fSPaolo Bonzini     /* Max timeout for block erase */
67549ab747fSPaolo Bonzini     pfl->cfi_table[0x25] = 0x0A;
67649ab747fSPaolo Bonzini     /* Max timeout for chip erase */
67749ab747fSPaolo Bonzini     pfl->cfi_table[0x26] = 0x0D;
67849ab747fSPaolo Bonzini     /* Device size */
6791eb27d69SPhilippe Mathieu-Daudé     pfl->cfi_table[0x27] = ctz32(pfl->chip_len);
68049ab747fSPaolo Bonzini     /* Flash device interface (8 & 16 bits) */
68149ab747fSPaolo Bonzini     pfl->cfi_table[0x28] = 0x02;
68249ab747fSPaolo Bonzini     pfl->cfi_table[0x29] = 0x00;
68349ab747fSPaolo Bonzini     /* Max number of bytes in multi-bytes write */
68449ab747fSPaolo Bonzini     /* XXX: disable buffered write as it's not supported */
68549ab747fSPaolo Bonzini     //    pfl->cfi_table[0x2A] = 0x05;
68649ab747fSPaolo Bonzini     pfl->cfi_table[0x2A] = 0x00;
68749ab747fSPaolo Bonzini     pfl->cfi_table[0x2B] = 0x00;
68864659053SStephen Checkoway     /* Number of erase block regions */
68964659053SStephen Checkoway     pfl->cfi_table[0x2c] = nb_regions;
69064659053SStephen Checkoway     /* Erase block regions */
69164659053SStephen Checkoway     for (int i = 0; i < nb_regions; ++i) {
69264659053SStephen Checkoway         uint32_t sector_len_per_device = pfl->sector_len[i];
69364659053SStephen Checkoway         pfl->cfi_table[0x2d + 4 * i] = pfl->nb_blocs[i] - 1;
69464659053SStephen Checkoway         pfl->cfi_table[0x2e + 4 * i] = (pfl->nb_blocs[i] - 1) >> 8;
69564659053SStephen Checkoway         pfl->cfi_table[0x2f + 4 * i] = sector_len_per_device >> 8;
69664659053SStephen Checkoway         pfl->cfi_table[0x30 + 4 * i] = sector_len_per_device >> 16;
69764659053SStephen Checkoway     }
69864659053SStephen Checkoway     assert(0x2c + 4 * nb_regions < pri_ofs);
69949ab747fSPaolo Bonzini 
70049ab747fSPaolo Bonzini     /* Extended */
701d6874c83SPhilippe Mathieu-Daudé     pfl->cfi_table[0x00 + pri_ofs] = 'P';
702d6874c83SPhilippe Mathieu-Daudé     pfl->cfi_table[0x01 + pri_ofs] = 'R';
703d6874c83SPhilippe Mathieu-Daudé     pfl->cfi_table[0x02 + pri_ofs] = 'I';
70449ab747fSPaolo Bonzini 
7059ac45b88SPhilippe Mathieu-Daudé     /* Extended version 1.0 */
706d6874c83SPhilippe Mathieu-Daudé     pfl->cfi_table[0x03 + pri_ofs] = '1';
707d6874c83SPhilippe Mathieu-Daudé     pfl->cfi_table[0x04 + pri_ofs] = '0';
70849ab747fSPaolo Bonzini 
7099ac45b88SPhilippe Mathieu-Daudé     /* Address sensitive unlock required. */
710d6874c83SPhilippe Mathieu-Daudé     pfl->cfi_table[0x05 + pri_ofs] = 0x00;
7119ac45b88SPhilippe Mathieu-Daudé     /* Erase suspend not supported. */
712d6874c83SPhilippe Mathieu-Daudé     pfl->cfi_table[0x06 + pri_ofs] = 0x00;
7139ac45b88SPhilippe Mathieu-Daudé     /* Sector protect not supported. */
714d6874c83SPhilippe Mathieu-Daudé     pfl->cfi_table[0x07 + pri_ofs] = 0x00;
7159ac45b88SPhilippe Mathieu-Daudé     /* Temporary sector unprotect not supported. */
716d6874c83SPhilippe Mathieu-Daudé     pfl->cfi_table[0x08 + pri_ofs] = 0x00;
71749ab747fSPaolo Bonzini 
7189ac45b88SPhilippe Mathieu-Daudé     /* Sector protect/unprotect scheme. */
719d6874c83SPhilippe Mathieu-Daudé     pfl->cfi_table[0x09 + pri_ofs] = 0x00;
72049ab747fSPaolo Bonzini 
7219ac45b88SPhilippe Mathieu-Daudé     /* Simultaneous operation not supported. */
722d6874c83SPhilippe Mathieu-Daudé     pfl->cfi_table[0x0a + pri_ofs] = 0x00;
7239ac45b88SPhilippe Mathieu-Daudé     /* Burst mode not supported. */
724d6874c83SPhilippe Mathieu-Daudé     pfl->cfi_table[0x0b + pri_ofs] = 0x00;
725c2c1bf44SPhilippe Mathieu-Daudé     /* Page mode not supported. */
726c2c1bf44SPhilippe Mathieu-Daudé     pfl->cfi_table[0x0c + pri_ofs] = 0x00;
727c2c1bf44SPhilippe Mathieu-Daudé     assert(0x0c + pri_ofs < ARRAY_SIZE(pfl->cfi_table));
72849ab747fSPaolo Bonzini }
72949ab747fSPaolo Bonzini 
73049ab747fSPaolo Bonzini static Property pflash_cfi02_properties[] = {
73116434065SMarkus Armbruster     DEFINE_PROP_DRIVE("drive", PFlashCFI02, blk),
73264659053SStephen Checkoway     DEFINE_PROP_UINT32("num-blocks", PFlashCFI02, uniform_nb_blocs, 0),
73364659053SStephen Checkoway     DEFINE_PROP_UINT32("sector-length", PFlashCFI02, uniform_sector_len, 0),
73464659053SStephen Checkoway     DEFINE_PROP_UINT32("num-blocks0", PFlashCFI02, nb_blocs[0], 0),
73564659053SStephen Checkoway     DEFINE_PROP_UINT32("sector-length0", PFlashCFI02, sector_len[0], 0),
73664659053SStephen Checkoway     DEFINE_PROP_UINT32("num-blocks1", PFlashCFI02, nb_blocs[1], 0),
73764659053SStephen Checkoway     DEFINE_PROP_UINT32("sector-length1", PFlashCFI02, sector_len[1], 0),
73864659053SStephen Checkoway     DEFINE_PROP_UINT32("num-blocks2", PFlashCFI02, nb_blocs[2], 0),
73964659053SStephen Checkoway     DEFINE_PROP_UINT32("sector-length2", PFlashCFI02, sector_len[2], 0),
74064659053SStephen Checkoway     DEFINE_PROP_UINT32("num-blocks3", PFlashCFI02, nb_blocs[3], 0),
74164659053SStephen Checkoway     DEFINE_PROP_UINT32("sector-length3", PFlashCFI02, sector_len[3], 0),
74216434065SMarkus Armbruster     DEFINE_PROP_UINT8("width", PFlashCFI02, width, 0),
74316434065SMarkus Armbruster     DEFINE_PROP_UINT8("mappings", PFlashCFI02, mappings, 0),
74416434065SMarkus Armbruster     DEFINE_PROP_UINT8("big-endian", PFlashCFI02, be, 0),
74516434065SMarkus Armbruster     DEFINE_PROP_UINT16("id0", PFlashCFI02, ident0, 0),
74616434065SMarkus Armbruster     DEFINE_PROP_UINT16("id1", PFlashCFI02, ident1, 0),
74716434065SMarkus Armbruster     DEFINE_PROP_UINT16("id2", PFlashCFI02, ident2, 0),
74816434065SMarkus Armbruster     DEFINE_PROP_UINT16("id3", PFlashCFI02, ident3, 0),
74916434065SMarkus Armbruster     DEFINE_PROP_UINT16("unlock-addr0", PFlashCFI02, unlock_addr0, 0),
75016434065SMarkus Armbruster     DEFINE_PROP_UINT16("unlock-addr1", PFlashCFI02, unlock_addr1, 0),
75116434065SMarkus Armbruster     DEFINE_PROP_STRING("name", PFlashCFI02, name),
75249ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
75349ab747fSPaolo Bonzini };
75449ab747fSPaolo Bonzini 
755d80cf1ebSStephen Checkoway static void pflash_cfi02_unrealize(DeviceState *dev, Error **errp)
756d80cf1ebSStephen Checkoway {
757e7b62741SMarkus Armbruster     PFlashCFI02 *pfl = PFLASH_CFI02(dev);
758d80cf1ebSStephen Checkoway     timer_del(&pfl->timer);
759d80cf1ebSStephen Checkoway }
760d80cf1ebSStephen Checkoway 
76149ab747fSPaolo Bonzini static void pflash_cfi02_class_init(ObjectClass *klass, void *data)
76249ab747fSPaolo Bonzini {
76349ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
76449ab747fSPaolo Bonzini 
765da3bd642SHu Tao     dc->realize = pflash_cfi02_realize;
766d80cf1ebSStephen Checkoway     dc->unrealize = pflash_cfi02_unrealize;
76749ab747fSPaolo Bonzini     dc->props = pflash_cfi02_properties;
768df6f9318SAntony Pavlov     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
76949ab747fSPaolo Bonzini }
77049ab747fSPaolo Bonzini 
77149ab747fSPaolo Bonzini static const TypeInfo pflash_cfi02_info = {
772e7b62741SMarkus Armbruster     .name           = TYPE_PFLASH_CFI02,
77349ab747fSPaolo Bonzini     .parent         = TYPE_SYS_BUS_DEVICE,
77416434065SMarkus Armbruster     .instance_size  = sizeof(PFlashCFI02),
77549ab747fSPaolo Bonzini     .class_init     = pflash_cfi02_class_init,
77649ab747fSPaolo Bonzini };
77749ab747fSPaolo Bonzini 
77849ab747fSPaolo Bonzini static void pflash_cfi02_register_types(void)
77949ab747fSPaolo Bonzini {
78049ab747fSPaolo Bonzini     type_register_static(&pflash_cfi02_info);
78149ab747fSPaolo Bonzini }
78249ab747fSPaolo Bonzini 
78349ab747fSPaolo Bonzini type_init(pflash_cfi02_register_types)
78449ab747fSPaolo Bonzini 
78516434065SMarkus Armbruster PFlashCFI02 *pflash_cfi02_register(hwaddr base,
786940d5b13SMarkus Armbruster                                    const char *name,
78749ab747fSPaolo Bonzini                                    hwaddr size,
78816434065SMarkus Armbruster                                    BlockBackend *blk,
789ce14710fSMarkus Armbruster                                    uint32_t sector_len,
79016434065SMarkus Armbruster                                    int nb_mappings, int width,
79149ab747fSPaolo Bonzini                                    uint16_t id0, uint16_t id1,
79249ab747fSPaolo Bonzini                                    uint16_t id2, uint16_t id3,
79316434065SMarkus Armbruster                                    uint16_t unlock_addr0,
79416434065SMarkus Armbruster                                    uint16_t unlock_addr1,
79549ab747fSPaolo Bonzini                                    int be)
79649ab747fSPaolo Bonzini {
797e7b62741SMarkus Armbruster     DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI02);
79849ab747fSPaolo Bonzini 
7999b3d111aSMarkus Armbruster     if (blk) {
8009b3d111aSMarkus Armbruster         qdev_prop_set_drive(dev, "drive", blk, &error_abort);
80149ab747fSPaolo Bonzini     }
802ce14710fSMarkus Armbruster     assert(size % sector_len == 0);
803ce14710fSMarkus Armbruster     qdev_prop_set_uint32(dev, "num-blocks", size / sector_len);
80449ab747fSPaolo Bonzini     qdev_prop_set_uint32(dev, "sector-length", sector_len);
80549ab747fSPaolo Bonzini     qdev_prop_set_uint8(dev, "width", width);
80649ab747fSPaolo Bonzini     qdev_prop_set_uint8(dev, "mappings", nb_mappings);
80749ab747fSPaolo Bonzini     qdev_prop_set_uint8(dev, "big-endian", !!be);
80849ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id0", id0);
80949ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id1", id1);
81049ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id2", id2);
81149ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id3", id3);
81249ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "unlock-addr0", unlock_addr0);
81349ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "unlock-addr1", unlock_addr1);
81449ab747fSPaolo Bonzini     qdev_prop_set_string(dev, "name", name);
81549ab747fSPaolo Bonzini     qdev_init_nofail(dev);
81649ab747fSPaolo Bonzini 
8173509c396SHu Tao     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
818e7b62741SMarkus Armbruster     return PFLASH_CFI02(dev);
81949ab747fSPaolo Bonzini }
820