xref: /openbmc/qemu/hw/block/pflash_cfi01.c (revision 63dc36944383f70f1c7a20f6104966d8560300fa)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  *  CFI parallel flash with Intel command set emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  *  Copyright (c) 2006 Thorsten Zitterell
549ab747fSPaolo Bonzini  *  Copyright (c) 2005 Jocelyn Mayer
649ab747fSPaolo Bonzini  *
749ab747fSPaolo Bonzini  * This library is free software; you can redistribute it and/or
849ab747fSPaolo Bonzini  * modify it under the terms of the GNU Lesser General Public
949ab747fSPaolo Bonzini  * License as published by the Free Software Foundation; either
103564a919SChetan Pant  * version 2.1 of the License, or (at your option) any later version.
1149ab747fSPaolo Bonzini  *
1249ab747fSPaolo Bonzini  * This library is distributed in the hope that it will be useful,
1349ab747fSPaolo Bonzini  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1449ab747fSPaolo Bonzini  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1549ab747fSPaolo Bonzini  * Lesser General Public License for more details.
1649ab747fSPaolo Bonzini  *
1749ab747fSPaolo Bonzini  * You should have received a copy of the GNU Lesser General Public
1849ab747fSPaolo Bonzini  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
1949ab747fSPaolo Bonzini  */
2049ab747fSPaolo Bonzini 
2149ab747fSPaolo Bonzini /*
2249ab747fSPaolo Bonzini  * For now, this code can emulate flashes of 1, 2 or 4 bytes width.
2349ab747fSPaolo Bonzini  * Supported commands/modes are:
2449ab747fSPaolo Bonzini  * - flash read
2549ab747fSPaolo Bonzini  * - flash write
2649ab747fSPaolo Bonzini  * - flash ID read
2749ab747fSPaolo Bonzini  * - sector erase
2849ab747fSPaolo Bonzini  * - CFI queries
2949ab747fSPaolo Bonzini  *
3049ab747fSPaolo Bonzini  * It does not support timings
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  * It does not implement much more ...
3749ab747fSPaolo Bonzini  */
3849ab747fSPaolo Bonzini 
3980c71a24SPeter Maydell #include "qemu/osdep.h"
4006f15217SMarkus Armbruster #include "hw/block/block.h"
4149ab747fSPaolo Bonzini #include "hw/block/flash.h"
42a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
43ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
444be74634SMarkus Armbruster #include "sysemu/block-backend.h"
45da34e65cSMarkus Armbruster #include "qapi/error.h"
461857b9dbSMansour Ahmadi #include "qemu/error-report.h"
471997b485SRoy Franz #include "qemu/bitops.h"
4849ab747fSPaolo Bonzini #include "qemu/host-utils.h"
4903dd024fSPaolo Bonzini #include "qemu/log.h"
502d731dbdSMarkus Armbruster #include "qemu/option.h"
5149ab747fSPaolo Bonzini #include "hw/sysbus.h"
52d6454270SMarkus Armbruster #include "migration/vmstate.h"
532d731dbdSMarkus Armbruster #include "sysemu/blockdev.h"
5454d31236SMarkus Armbruster #include "sysemu/runstate.h"
5513019f1fSPhilippe Mathieu-Daudé #include "trace.h"
5649ab747fSPaolo Bonzini 
57e9809422SPaolo Bonzini #define PFLASH_BE          0
58f71e42a5SPaolo Bonzini #define PFLASH_SECURE      1
59e9809422SPaolo Bonzini 
6016434065SMarkus Armbruster struct PFlashCFI01 {
61f1b44f0eSHu Tao     /*< private >*/
62f1b44f0eSHu Tao     SysBusDevice parent_obj;
63f1b44f0eSHu Tao     /*< public >*/
64f1b44f0eSHu Tao 
654be74634SMarkus Armbruster     BlockBackend *blk;
6649ab747fSPaolo Bonzini     uint32_t nb_blocs;
6749ab747fSPaolo Bonzini     uint64_t sector_len;
684b6fedcaSRoy Franz     uint8_t bank_width;
691997b485SRoy Franz     uint8_t device_width; /* If 0, device width not specified. */
70fa21a7b1SRoy Franz     uint8_t max_device_width;  /* max device width in bytes */
71e9809422SPaolo Bonzini     uint32_t features;
7249ab747fSPaolo Bonzini     uint8_t wcycle; /* if 0, the flash is read normally */
732231bee2SDavid Edmondson     bool ro;
7449ab747fSPaolo Bonzini     uint8_t cmd;
7549ab747fSPaolo Bonzini     uint8_t status;
7649ab747fSPaolo Bonzini     uint16_t ident0;
7749ab747fSPaolo Bonzini     uint16_t ident1;
7849ab747fSPaolo Bonzini     uint16_t ident2;
7949ab747fSPaolo Bonzini     uint16_t ident3;
8049ab747fSPaolo Bonzini     uint8_t cfi_table[0x52];
8149ab747fSPaolo Bonzini     uint64_t counter;
82284a7ee2SGerd Hoffmann     uint32_t writeblock_size;
8349ab747fSPaolo Bonzini     MemoryRegion mem;
8449ab747fSPaolo Bonzini     char *name;
8549ab747fSPaolo Bonzini     void *storage;
8690c647dbSDr. David Alan Gilbert     VMChangeStateEntry *vmstate;
87feb0b1aaSPeter Maydell     bool old_multiple_chip_handling;
88284a7ee2SGerd Hoffmann 
89284a7ee2SGerd Hoffmann     /* block update buffer */
90284a7ee2SGerd Hoffmann     unsigned char *blk_bytes;
91284a7ee2SGerd Hoffmann     uint32_t blk_offset;
9249ab747fSPaolo Bonzini };
9349ab747fSPaolo Bonzini 
944c0cfc72SLaszlo Ersek static int pflash_post_load(void *opaque, int version_id);
954c0cfc72SLaszlo Ersek 
pflash_blk_write_state_needed(void * opaque)96284a7ee2SGerd Hoffmann static bool pflash_blk_write_state_needed(void *opaque)
97284a7ee2SGerd Hoffmann {
98284a7ee2SGerd Hoffmann     PFlashCFI01 *pfl = opaque;
99284a7ee2SGerd Hoffmann 
100284a7ee2SGerd Hoffmann     return (pfl->blk_offset != -1);
101284a7ee2SGerd Hoffmann }
102284a7ee2SGerd Hoffmann 
103284a7ee2SGerd Hoffmann static const VMStateDescription vmstate_pflash_blk_write = {
104284a7ee2SGerd Hoffmann     .name = "pflash_cfi01_blk_write",
105284a7ee2SGerd Hoffmann     .version_id = 1,
106284a7ee2SGerd Hoffmann     .minimum_version_id = 1,
107284a7ee2SGerd Hoffmann     .needed = pflash_blk_write_state_needed,
108284a7ee2SGerd Hoffmann     .fields = (const VMStateField[]) {
109284a7ee2SGerd Hoffmann         VMSTATE_VBUFFER_UINT32(blk_bytes, PFlashCFI01, 0, NULL, writeblock_size),
110284a7ee2SGerd Hoffmann         VMSTATE_UINT32(blk_offset, PFlashCFI01),
111284a7ee2SGerd Hoffmann         VMSTATE_END_OF_LIST()
112284a7ee2SGerd Hoffmann     }
113284a7ee2SGerd Hoffmann };
114284a7ee2SGerd Hoffmann 
11549ab747fSPaolo Bonzini static const VMStateDescription vmstate_pflash = {
11649ab747fSPaolo Bonzini     .name = "pflash_cfi01",
11749ab747fSPaolo Bonzini     .version_id = 1,
11849ab747fSPaolo Bonzini     .minimum_version_id = 1,
1194c0cfc72SLaszlo Ersek     .post_load = pflash_post_load,
1207d5dc0a3SRichard Henderson     .fields = (const VMStateField[]) {
12116434065SMarkus Armbruster         VMSTATE_UINT8(wcycle, PFlashCFI01),
12216434065SMarkus Armbruster         VMSTATE_UINT8(cmd, PFlashCFI01),
12316434065SMarkus Armbruster         VMSTATE_UINT8(status, PFlashCFI01),
12416434065SMarkus Armbruster         VMSTATE_UINT64(counter, PFlashCFI01),
12549ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
126284a7ee2SGerd Hoffmann     },
127284a7ee2SGerd Hoffmann     .subsections = (const VMStateDescription * const []) {
128284a7ee2SGerd Hoffmann         &vmstate_pflash_blk_write,
129284a7ee2SGerd Hoffmann         NULL
13049ab747fSPaolo Bonzini     }
13149ab747fSPaolo Bonzini };
13249ab747fSPaolo Bonzini 
133ccd8014bSPhilippe Mathieu-Daudé /*
134ccd8014bSPhilippe Mathieu-Daudé  * Perform a CFI query based on the bank width of the flash.
1354433e660SRoy Franz  * If this code is called we know we have a device_width set for
1364433e660SRoy Franz  * this flash.
1374433e660SRoy Franz  */
pflash_cfi_query(PFlashCFI01 * pfl,hwaddr offset)13816434065SMarkus Armbruster static uint32_t pflash_cfi_query(PFlashCFI01 *pfl, hwaddr offset)
1394433e660SRoy Franz {
1404433e660SRoy Franz     int i;
1414433e660SRoy Franz     uint32_t resp = 0;
1424433e660SRoy Franz     hwaddr boff;
1434433e660SRoy Franz 
144ccd8014bSPhilippe Mathieu-Daudé     /*
145ccd8014bSPhilippe Mathieu-Daudé      * Adjust incoming offset to match expected device-width
1464433e660SRoy Franz      * addressing. CFI query addresses are always specified in terms of
1474433e660SRoy Franz      * the maximum supported width of the device.  This means that x8
1484433e660SRoy Franz      * devices and x8/x16 devices in x8 mode behave differently.  For
1494433e660SRoy Franz      * devices that are not used at their max width, we will be
1504433e660SRoy Franz      * provided with addresses that use higher address bits than
1514433e660SRoy Franz      * expected (based on the max width), so we will shift them lower
1524433e660SRoy Franz      * so that they will match the addresses used when
1534433e660SRoy Franz      * device_width==max_device_width.
1544433e660SRoy Franz      */
1554433e660SRoy Franz     boff = offset >> (ctz32(pfl->bank_width) +
1564433e660SRoy Franz                       ctz32(pfl->max_device_width) - ctz32(pfl->device_width));
1574433e660SRoy Franz 
15807c13a71SPhilippe Mathieu-Daudé     if (boff >= sizeof(pfl->cfi_table)) {
1594433e660SRoy Franz         return 0;
1604433e660SRoy Franz     }
161ccd8014bSPhilippe Mathieu-Daudé     /*
162ccd8014bSPhilippe Mathieu-Daudé      * Now we will construct the CFI response generated by a single
1634433e660SRoy Franz      * device, then replicate that for all devices that make up the
1644433e660SRoy Franz      * bus.  For wide parts used in x8 mode, CFI query responses
1654433e660SRoy Franz      * are different than native byte-wide parts.
1664433e660SRoy Franz      */
1674433e660SRoy Franz     resp = pfl->cfi_table[boff];
1684433e660SRoy Franz     if (pfl->device_width != pfl->max_device_width) {
1694433e660SRoy Franz         /* The only case currently supported is x8 mode for a
1704433e660SRoy Franz          * wider part.
1714433e660SRoy Franz          */
1724433e660SRoy Franz         if (pfl->device_width != 1 || pfl->bank_width > 4) {
17391316cbbSDavid Edmondson             trace_pflash_unsupported_device_configuration(pfl->name,
17491316cbbSDavid Edmondson                                     pfl->device_width, pfl->max_device_width);
1754433e660SRoy Franz             return 0;
1764433e660SRoy Franz         }
1774433e660SRoy Franz         /* CFI query data is repeated, rather than zero padded for
1784433e660SRoy Franz          * wide devices used in x8 mode.
1794433e660SRoy Franz          */
1804433e660SRoy Franz         for (i = 1; i < pfl->max_device_width; i++) {
1814433e660SRoy Franz             resp = deposit32(resp, 8 * i, 8, pfl->cfi_table[boff]);
1824433e660SRoy Franz         }
1834433e660SRoy Franz     }
1844433e660SRoy Franz     /* Replicate responses for each device in bank. */
1854433e660SRoy Franz     if (pfl->device_width < pfl->bank_width) {
1864433e660SRoy Franz         for (i = pfl->device_width;
1874433e660SRoy Franz              i < pfl->bank_width; i += pfl->device_width) {
1884433e660SRoy Franz             resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp);
1894433e660SRoy Franz         }
1904433e660SRoy Franz     }
1914433e660SRoy Franz 
1924433e660SRoy Franz     return resp;
1934433e660SRoy Franz }
1944433e660SRoy Franz 
1950163a2dcSRoy Franz 
1960163a2dcSRoy Franz 
1970163a2dcSRoy Franz /* Perform a device id query based on the bank width of the flash. */
pflash_devid_query(PFlashCFI01 * pfl,hwaddr offset)19816434065SMarkus Armbruster static uint32_t pflash_devid_query(PFlashCFI01 *pfl, hwaddr offset)
1990163a2dcSRoy Franz {
2000163a2dcSRoy Franz     int i;
2010163a2dcSRoy Franz     uint32_t resp;
2020163a2dcSRoy Franz     hwaddr boff;
2030163a2dcSRoy Franz 
204ccd8014bSPhilippe Mathieu-Daudé     /*
205ccd8014bSPhilippe Mathieu-Daudé      * Adjust incoming offset to match expected device-width
2060163a2dcSRoy Franz      * addressing. Device ID read addresses are always specified in
2070163a2dcSRoy Franz      * terms of the maximum supported width of the device.  This means
2080163a2dcSRoy Franz      * that x8 devices and x8/x16 devices in x8 mode behave
2090163a2dcSRoy Franz      * differently. For devices that are not used at their max width,
2100163a2dcSRoy Franz      * we will be provided with addresses that use higher address bits
2110163a2dcSRoy Franz      * than expected (based on the max width), so we will shift them
2120163a2dcSRoy Franz      * lower so that they will match the addresses used when
2130163a2dcSRoy Franz      * device_width==max_device_width.
2140163a2dcSRoy Franz      */
2150163a2dcSRoy Franz     boff = offset >> (ctz32(pfl->bank_width) +
2160163a2dcSRoy Franz                       ctz32(pfl->max_device_width) - ctz32(pfl->device_width));
2170163a2dcSRoy Franz 
218ccd8014bSPhilippe Mathieu-Daudé     /*
219ccd8014bSPhilippe Mathieu-Daudé      * Mask off upper bits which may be used in to query block
2200163a2dcSRoy Franz      * or sector lock status at other addresses.
2210163a2dcSRoy Franz      * Offsets 2/3 are block lock status, is not emulated.
2220163a2dcSRoy Franz      */
2230163a2dcSRoy Franz     switch (boff & 0xFF) {
2240163a2dcSRoy Franz     case 0:
2250163a2dcSRoy Franz         resp = pfl->ident0;
22691316cbbSDavid Edmondson         trace_pflash_manufacturer_id(pfl->name, resp);
2270163a2dcSRoy Franz         break;
2280163a2dcSRoy Franz     case 1:
2290163a2dcSRoy Franz         resp = pfl->ident1;
23091316cbbSDavid Edmondson         trace_pflash_device_id(pfl->name, resp);
2310163a2dcSRoy Franz         break;
2320163a2dcSRoy Franz     default:
23391316cbbSDavid Edmondson         trace_pflash_device_info(pfl->name, offset);
2340163a2dcSRoy Franz         return 0;
2350163a2dcSRoy Franz     }
2360163a2dcSRoy Franz     /* Replicate responses for each device in bank. */
2370163a2dcSRoy Franz     if (pfl->device_width < pfl->bank_width) {
2380163a2dcSRoy Franz         for (i = pfl->device_width;
2390163a2dcSRoy Franz               i < pfl->bank_width; i += pfl->device_width) {
2400163a2dcSRoy Franz             resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp);
2410163a2dcSRoy Franz         }
2420163a2dcSRoy Franz     }
2430163a2dcSRoy Franz 
2440163a2dcSRoy Franz     return resp;
2450163a2dcSRoy Franz }
2460163a2dcSRoy Franz 
pflash_data_read(PFlashCFI01 * pfl,hwaddr offset,int width,int be)24716434065SMarkus Armbruster static uint32_t pflash_data_read(PFlashCFI01 *pfl, hwaddr offset,
24849ab747fSPaolo Bonzini                                  int width, int be)
24949ab747fSPaolo Bonzini {
25049ab747fSPaolo Bonzini     uint8_t *p;
251f71e42a5SPaolo Bonzini     uint32_t ret;
25249ab747fSPaolo Bonzini 
25349ab747fSPaolo Bonzini     p = pfl->storage;
25449ab747fSPaolo Bonzini     if (be) {
2555dd58358SGerd Hoffmann         ret = ldn_be_p(p + offset, width);
25649ab747fSPaolo Bonzini     } else {
2575dd58358SGerd Hoffmann         ret = ldn_le_p(p + offset, width);
258f71e42a5SPaolo Bonzini     }
25991316cbbSDavid Edmondson     trace_pflash_data_read(pfl->name, offset, width, ret);
260f71e42a5SPaolo Bonzini     return ret;
26149ab747fSPaolo Bonzini }
26249ab747fSPaolo Bonzini 
pflash_read(PFlashCFI01 * pfl,hwaddr offset,int width,int be)26316434065SMarkus Armbruster static uint32_t pflash_read(PFlashCFI01 *pfl, hwaddr offset,
264f71e42a5SPaolo Bonzini                             int width, int be)
265f71e42a5SPaolo Bonzini {
266f71e42a5SPaolo Bonzini     hwaddr boff;
267f71e42a5SPaolo Bonzini     uint32_t ret;
268f71e42a5SPaolo Bonzini 
269f71e42a5SPaolo Bonzini     ret = -1;
270f71e42a5SPaolo Bonzini     switch (pfl->cmd) {
271f71e42a5SPaolo Bonzini     default:
272f71e42a5SPaolo Bonzini         /* This should never happen : reset state & treat it as a read */
27391316cbbSDavid Edmondson         trace_pflash_read_unknown_state(pfl->name, pfl->cmd);
274f71e42a5SPaolo Bonzini         pfl->wcycle = 0;
275aba53a12SPhilippe Mathieu-Daudé         /*
276aba53a12SPhilippe Mathieu-Daudé          * The command 0x00 is not assigned by the CFI open standard,
277aba53a12SPhilippe Mathieu-Daudé          * but QEMU historically uses it for the READ_ARRAY command (0xff).
278aba53a12SPhilippe Mathieu-Daudé          */
279aba53a12SPhilippe Mathieu-Daudé         pfl->cmd = 0x00;
280f71e42a5SPaolo Bonzini         /* fall through to read code */
281aba53a12SPhilippe Mathieu-Daudé     case 0x00: /* This model reset value for READ_ARRAY (not CFI compliant) */
282f71e42a5SPaolo Bonzini         /* Flash area read */
283f71e42a5SPaolo Bonzini         ret = pflash_data_read(pfl, offset, width, be);
28449ab747fSPaolo Bonzini         break;
28549ab747fSPaolo Bonzini     case 0x10: /* Single byte program */
28649ab747fSPaolo Bonzini     case 0x20: /* Block erase */
28749ab747fSPaolo Bonzini     case 0x28: /* Block erase */
28849ab747fSPaolo Bonzini     case 0x40: /* single byte program */
28949ab747fSPaolo Bonzini     case 0x50: /* Clear status register */
29049ab747fSPaolo Bonzini     case 0x60: /* Block /un)lock */
29149ab747fSPaolo Bonzini     case 0x70: /* Status Register */
29249ab747fSPaolo Bonzini     case 0xe8: /* Write block */
293ccd8014bSPhilippe Mathieu-Daudé         /*
294ccd8014bSPhilippe Mathieu-Daudé          * Status register read.  Return status from each device in
2952003889fSRoy Franz          * bank.
2962003889fSRoy Franz          */
29749ab747fSPaolo Bonzini         ret = pfl->status;
2982003889fSRoy Franz         if (pfl->device_width && width > pfl->device_width) {
2992003889fSRoy Franz             int shift = pfl->device_width * 8;
3002003889fSRoy Franz             while (shift + pfl->device_width * 8 <= width * 8) {
3012003889fSRoy Franz                 ret |= pfl->status << shift;
3022003889fSRoy Franz                 shift += pfl->device_width * 8;
3032003889fSRoy Franz             }
3042003889fSRoy Franz         } else if (!pfl->device_width && width > 2) {
305ccd8014bSPhilippe Mathieu-Daudé             /*
306ccd8014bSPhilippe Mathieu-Daudé              * Handle 32 bit flash cases where device width is not
3072003889fSRoy Franz              * set. (Existing behavior before device width added.)
3082003889fSRoy Franz              */
309ea0a4f34SPaul Burton             ret |= pfl->status << 16;
310ea0a4f34SPaul Burton         }
31191316cbbSDavid Edmondson         trace_pflash_read_status(pfl->name, ret);
31249ab747fSPaolo Bonzini         break;
31349ab747fSPaolo Bonzini     case 0x90:
3140163a2dcSRoy Franz         if (!pfl->device_width) {
3150163a2dcSRoy Franz             /* Preserve old behavior if device width not specified */
3164433e660SRoy Franz             boff = offset & 0xFF;
3174433e660SRoy Franz             if (pfl->bank_width == 2) {
3184433e660SRoy Franz                 boff = boff >> 1;
3194433e660SRoy Franz             } else if (pfl->bank_width == 4) {
3204433e660SRoy Franz                 boff = boff >> 2;
3214433e660SRoy Franz             }
3224433e660SRoy Franz 
32349ab747fSPaolo Bonzini             switch (boff) {
32449ab747fSPaolo Bonzini             case 0:
32549ab747fSPaolo Bonzini                 ret = pfl->ident0 << 8 | pfl->ident1;
32691316cbbSDavid Edmondson                 trace_pflash_manufacturer_id(pfl->name, ret);
32749ab747fSPaolo Bonzini                 break;
32849ab747fSPaolo Bonzini             case 1:
32949ab747fSPaolo Bonzini                 ret = pfl->ident2 << 8 | pfl->ident3;
33091316cbbSDavid Edmondson                 trace_pflash_device_id(pfl->name, ret);
33149ab747fSPaolo Bonzini                 break;
33249ab747fSPaolo Bonzini             default:
33391316cbbSDavid Edmondson                 trace_pflash_device_info(pfl->name, boff);
33449ab747fSPaolo Bonzini                 ret = 0;
33549ab747fSPaolo Bonzini                 break;
33649ab747fSPaolo Bonzini             }
3370163a2dcSRoy Franz         } else {
338ccd8014bSPhilippe Mathieu-Daudé             /*
339ccd8014bSPhilippe Mathieu-Daudé              * If we have a read larger than the bank_width, combine multiple
3400163a2dcSRoy Franz              * manufacturer/device ID queries into a single response.
3410163a2dcSRoy Franz              */
3420163a2dcSRoy Franz             int i;
3430163a2dcSRoy Franz             for (i = 0; i < width; i += pfl->bank_width) {
3440163a2dcSRoy Franz                 ret = deposit32(ret, i * 8, pfl->bank_width * 8,
3450163a2dcSRoy Franz                                 pflash_devid_query(pfl,
3460163a2dcSRoy Franz                                                  offset + i * pfl->bank_width));
3470163a2dcSRoy Franz             }
3480163a2dcSRoy Franz         }
34949ab747fSPaolo Bonzini         break;
35049ab747fSPaolo Bonzini     case 0x98: /* Query mode */
3514433e660SRoy Franz         if (!pfl->device_width) {
3524433e660SRoy Franz             /* Preserve old behavior if device width not specified */
3534433e660SRoy Franz             boff = offset & 0xFF;
3544433e660SRoy Franz             if (pfl->bank_width == 2) {
3554433e660SRoy Franz                 boff = boff >> 1;
3564433e660SRoy Franz             } else if (pfl->bank_width == 4) {
3574433e660SRoy Franz                 boff = boff >> 2;
3584433e660SRoy Franz             }
3594433e660SRoy Franz 
36007c13a71SPhilippe Mathieu-Daudé             if (boff < sizeof(pfl->cfi_table)) {
36149ab747fSPaolo Bonzini                 ret = pfl->cfi_table[boff];
36207c13a71SPhilippe Mathieu-Daudé             } else {
36307c13a71SPhilippe Mathieu-Daudé                 ret = 0;
3644433e660SRoy Franz             }
3654433e660SRoy Franz         } else {
366ccd8014bSPhilippe Mathieu-Daudé             /*
367ccd8014bSPhilippe Mathieu-Daudé              * If we have a read larger than the bank_width, combine multiple
3684433e660SRoy Franz              * CFI queries into a single response.
3694433e660SRoy Franz              */
3704433e660SRoy Franz             int i;
3714433e660SRoy Franz             for (i = 0; i < width; i += pfl->bank_width) {
3724433e660SRoy Franz                 ret = deposit32(ret, i * 8, pfl->bank_width * 8,
3734433e660SRoy Franz                                 pflash_cfi_query(pfl,
3744433e660SRoy Franz                                                  offset + i * pfl->bank_width));
3754433e660SRoy Franz             }
3764433e660SRoy Franz         }
3774433e660SRoy Franz 
37849ab747fSPaolo Bonzini         break;
37949ab747fSPaolo Bonzini     }
38091316cbbSDavid Edmondson     trace_pflash_io_read(pfl->name, offset, width, ret, pfl->cmd, pfl->wcycle);
381e8aa2d95SPhilippe Mathieu-Daudé 
38249ab747fSPaolo Bonzini     return ret;
38349ab747fSPaolo Bonzini }
38449ab747fSPaolo Bonzini 
38549ab747fSPaolo Bonzini /* update flash content on disk */
pflash_update(PFlashCFI01 * pfl,int offset,int size)38616434065SMarkus Armbruster static void pflash_update(PFlashCFI01 *pfl, int offset,
38749ab747fSPaolo Bonzini                           int size)
38849ab747fSPaolo Bonzini {
38949ab747fSPaolo Bonzini     int offset_end;
3901857b9dbSMansour Ahmadi     int ret;
3914be74634SMarkus Armbruster     if (pfl->blk) {
39249ab747fSPaolo Bonzini         offset_end = offset + size;
393098e732dSEric Blake         /* widen to sector boundaries */
394098e732dSEric Blake         offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE);
395098e732dSEric Blake         offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE);
396a9262f55SAlberto Faria         ret = blk_pwrite(pfl->blk, offset, offset_end - offset,
397a9262f55SAlberto Faria                          pfl->storage + offset, 0);
3981857b9dbSMansour Ahmadi         if (ret < 0) {
3991857b9dbSMansour Ahmadi             /* TODO set error bit in status */
4001857b9dbSMansour Ahmadi             error_report("Could not update PFLASH: %s", strerror(-ret));
4011857b9dbSMansour Ahmadi         }
40249ab747fSPaolo Bonzini     }
40349ab747fSPaolo Bonzini }
40449ab747fSPaolo Bonzini 
405284a7ee2SGerd Hoffmann /* copy current flash content to block update buffer */
pflash_blk_write_start(PFlashCFI01 * pfl,hwaddr offset)406284a7ee2SGerd Hoffmann static void pflash_blk_write_start(PFlashCFI01 *pfl, hwaddr offset)
407284a7ee2SGerd Hoffmann {
408284a7ee2SGerd Hoffmann     hwaddr mask = ~(pfl->writeblock_size - 1);
409284a7ee2SGerd Hoffmann 
410284a7ee2SGerd Hoffmann     trace_pflash_write_block_start(pfl->name, pfl->counter);
411284a7ee2SGerd Hoffmann     pfl->blk_offset = offset & mask;
412284a7ee2SGerd Hoffmann     memcpy(pfl->blk_bytes, pfl->storage + pfl->blk_offset,
413284a7ee2SGerd Hoffmann            pfl->writeblock_size);
414284a7ee2SGerd Hoffmann }
415284a7ee2SGerd Hoffmann 
416284a7ee2SGerd Hoffmann /* commit block update buffer changes */
pflash_blk_write_flush(PFlashCFI01 * pfl)417284a7ee2SGerd Hoffmann static void pflash_blk_write_flush(PFlashCFI01 *pfl)
418284a7ee2SGerd Hoffmann {
419284a7ee2SGerd Hoffmann     g_assert(pfl->blk_offset != -1);
420284a7ee2SGerd Hoffmann     trace_pflash_write_block_flush(pfl->name);
421284a7ee2SGerd Hoffmann     memcpy(pfl->storage + pfl->blk_offset, pfl->blk_bytes,
422284a7ee2SGerd Hoffmann            pfl->writeblock_size);
423284a7ee2SGerd Hoffmann     pflash_update(pfl, pfl->blk_offset, pfl->writeblock_size);
424284a7ee2SGerd Hoffmann     pfl->blk_offset = -1;
425284a7ee2SGerd Hoffmann }
426284a7ee2SGerd Hoffmann 
427284a7ee2SGerd Hoffmann /* discard block update buffer changes */
pflash_blk_write_abort(PFlashCFI01 * pfl)428284a7ee2SGerd Hoffmann static void pflash_blk_write_abort(PFlashCFI01 *pfl)
429284a7ee2SGerd Hoffmann {
430284a7ee2SGerd Hoffmann     trace_pflash_write_block_abort(pfl->name);
431284a7ee2SGerd Hoffmann     pfl->blk_offset = -1;
432284a7ee2SGerd Hoffmann }
433284a7ee2SGerd Hoffmann 
pflash_data_write(PFlashCFI01 * pfl,hwaddr offset,uint32_t value,int width,int be)43416434065SMarkus Armbruster static inline void pflash_data_write(PFlashCFI01 *pfl, hwaddr offset,
43549ab747fSPaolo Bonzini                                      uint32_t value, int width, int be)
43649ab747fSPaolo Bonzini {
4373b14a555SGerd Hoffmann     uint8_t *p;
43849ab747fSPaolo Bonzini 
439284a7ee2SGerd Hoffmann     if (pfl->blk_offset != -1) {
440284a7ee2SGerd Hoffmann         /* block write: redirect writes to block update buffer */
441284a7ee2SGerd Hoffmann         if ((offset < pfl->blk_offset) ||
442284a7ee2SGerd Hoffmann             (offset + width > pfl->blk_offset + pfl->writeblock_size)) {
443284a7ee2SGerd Hoffmann             pfl->status |= 0x10; /* Programming error */
444284a7ee2SGerd Hoffmann             return;
445284a7ee2SGerd Hoffmann         }
446284a7ee2SGerd Hoffmann         trace_pflash_data_write_block(pfl->name, offset, width, value,
447284a7ee2SGerd Hoffmann                                       pfl->counter);
448284a7ee2SGerd Hoffmann         p = pfl->blk_bytes + (offset - pfl->blk_offset);
449284a7ee2SGerd Hoffmann     } else {
450284a7ee2SGerd Hoffmann         /* write directly to storage */
451284a7ee2SGerd Hoffmann         trace_pflash_data_write(pfl->name, offset, width, value);
4523b14a555SGerd Hoffmann         p = pfl->storage + offset;
453284a7ee2SGerd Hoffmann     }
4543b14a555SGerd Hoffmann 
45549ab747fSPaolo Bonzini     if (be) {
4565dd58358SGerd Hoffmann         stn_be_p(p, width, value);
45749ab747fSPaolo Bonzini     } else {
4585dd58358SGerd Hoffmann         stn_le_p(p, width, value);
45949ab747fSPaolo Bonzini     }
46049ab747fSPaolo Bonzini }
46149ab747fSPaolo Bonzini 
pflash_write(PFlashCFI01 * pfl,hwaddr offset,uint32_t value,int width,int be)46216434065SMarkus Armbruster static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
46349ab747fSPaolo Bonzini                          uint32_t value, int width, int be)
46449ab747fSPaolo Bonzini {
46549ab747fSPaolo Bonzini     uint8_t *p;
46649ab747fSPaolo Bonzini     uint8_t cmd;
46749ab747fSPaolo Bonzini 
46849ab747fSPaolo Bonzini     cmd = value;
46949ab747fSPaolo Bonzini 
47091316cbbSDavid Edmondson     trace_pflash_io_write(pfl->name, offset, width, value, pfl->wcycle);
47149ab747fSPaolo Bonzini     if (!pfl->wcycle) {
47249ab747fSPaolo Bonzini         /* Set the device in I/O access mode */
4735f9a5ea1SJan Kiszka         memory_region_rom_device_set_romd(&pfl->mem, false);
47449ab747fSPaolo Bonzini     }
47549ab747fSPaolo Bonzini 
47649ab747fSPaolo Bonzini     switch (pfl->wcycle) {
47749ab747fSPaolo Bonzini     case 0:
47849ab747fSPaolo Bonzini         /* read mode */
47949ab747fSPaolo Bonzini         switch (cmd) {
480aba53a12SPhilippe Mathieu-Daudé         case 0x00: /* This model reset value for READ_ARRAY (not CFI) */
4813072182dSPhilippe Mathieu-Daudé             goto mode_read_array;
48249ab747fSPaolo Bonzini         case 0x10: /* Single Byte Program */
48349ab747fSPaolo Bonzini         case 0x40: /* Single Byte Program */
48491316cbbSDavid Edmondson             trace_pflash_write(pfl->name, "single byte program (0)");
48549ab747fSPaolo Bonzini             break;
48649ab747fSPaolo Bonzini         case 0x20: /* Block erase */
48749ab747fSPaolo Bonzini             p = pfl->storage;
48849ab747fSPaolo Bonzini             offset &= ~(pfl->sector_len - 1);
48949ab747fSPaolo Bonzini 
49091316cbbSDavid Edmondson             trace_pflash_write_block_erase(pfl->name, offset, pfl->sector_len);
49149ab747fSPaolo Bonzini 
49249ab747fSPaolo Bonzini             if (!pfl->ro) {
49349ab747fSPaolo Bonzini                 memset(p + offset, 0xff, pfl->sector_len);
49449ab747fSPaolo Bonzini                 pflash_update(pfl, offset, pfl->sector_len);
49549ab747fSPaolo Bonzini             } else {
49649ab747fSPaolo Bonzini                 pfl->status |= 0x20; /* Block erase error */
49749ab747fSPaolo Bonzini             }
49849ab747fSPaolo Bonzini             pfl->status |= 0x80; /* Ready! */
49949ab747fSPaolo Bonzini             break;
50049ab747fSPaolo Bonzini         case 0x50: /* Clear status bits */
50191316cbbSDavid Edmondson             trace_pflash_write(pfl->name, "clear status bits");
50249ab747fSPaolo Bonzini             pfl->status = 0x0;
5033072182dSPhilippe Mathieu-Daudé             goto mode_read_array;
50449ab747fSPaolo Bonzini         case 0x60: /* Block (un)lock */
50591316cbbSDavid Edmondson             trace_pflash_write(pfl->name, "block unlock");
50649ab747fSPaolo Bonzini             break;
50749ab747fSPaolo Bonzini         case 0x70: /* Status Register */
50891316cbbSDavid Edmondson             trace_pflash_write(pfl->name, "read status register");
50949ab747fSPaolo Bonzini             pfl->cmd = cmd;
51049ab747fSPaolo Bonzini             return;
51149ab747fSPaolo Bonzini         case 0x90: /* Read Device ID */
51291316cbbSDavid Edmondson             trace_pflash_write(pfl->name, "read device information");
51349ab747fSPaolo Bonzini             pfl->cmd = cmd;
51449ab747fSPaolo Bonzini             return;
51549ab747fSPaolo Bonzini         case 0x98: /* CFI query */
51691316cbbSDavid Edmondson             trace_pflash_write(pfl->name, "CFI query");
51749ab747fSPaolo Bonzini             break;
51849ab747fSPaolo Bonzini         case 0xe8: /* Write to buffer */
51991316cbbSDavid Edmondson             trace_pflash_write(pfl->name, "write to buffer");
52049ab747fSPaolo Bonzini             pfl->status |= 0x80; /* Ready! */
52149ab747fSPaolo Bonzini             break;
52249ab747fSPaolo Bonzini         case 0xf0: /* Probe for AMD flash */
52391316cbbSDavid Edmondson             trace_pflash_write(pfl->name, "probe for AMD flash");
5243072182dSPhilippe Mathieu-Daudé             goto mode_read_array;
5253072182dSPhilippe Mathieu-Daudé         case 0xff: /* Read Array */
52691316cbbSDavid Edmondson             trace_pflash_write(pfl->name, "read array mode");
5273072182dSPhilippe Mathieu-Daudé             goto mode_read_array;
52849ab747fSPaolo Bonzini         default:
52949ab747fSPaolo Bonzini             goto error_flash;
53049ab747fSPaolo Bonzini         }
53149ab747fSPaolo Bonzini         pfl->wcycle++;
53249ab747fSPaolo Bonzini         pfl->cmd = cmd;
53349ab747fSPaolo Bonzini         break;
53449ab747fSPaolo Bonzini     case 1:
53549ab747fSPaolo Bonzini         switch (pfl->cmd) {
53649ab747fSPaolo Bonzini         case 0x10: /* Single Byte Program */
53749ab747fSPaolo Bonzini         case 0x40: /* Single Byte Program */
53891316cbbSDavid Edmondson             trace_pflash_write(pfl->name, "single byte program (1)");
53949ab747fSPaolo Bonzini             if (!pfl->ro) {
54049ab747fSPaolo Bonzini                 pflash_data_write(pfl, offset, value, width, be);
54149ab747fSPaolo Bonzini                 pflash_update(pfl, offset, width);
54249ab747fSPaolo Bonzini             } else {
54349ab747fSPaolo Bonzini                 pfl->status |= 0x10; /* Programming error */
54449ab747fSPaolo Bonzini             }
54549ab747fSPaolo Bonzini             pfl->status |= 0x80; /* Ready! */
54649ab747fSPaolo Bonzini             pfl->wcycle = 0;
54749ab747fSPaolo Bonzini         break;
54849ab747fSPaolo Bonzini         case 0x20: /* Block erase */
54949ab747fSPaolo Bonzini         case 0x28:
55049ab747fSPaolo Bonzini             if (cmd == 0xd0) { /* confirm */
55149ab747fSPaolo Bonzini                 pfl->wcycle = 0;
55249ab747fSPaolo Bonzini                 pfl->status |= 0x80;
5533072182dSPhilippe Mathieu-Daudé             } else if (cmd == 0xff) { /* Read Array */
5543072182dSPhilippe Mathieu-Daudé                 goto mode_read_array;
55549ab747fSPaolo Bonzini             } else
55649ab747fSPaolo Bonzini                 goto error_flash;
55749ab747fSPaolo Bonzini 
55849ab747fSPaolo Bonzini             break;
55949ab747fSPaolo Bonzini         case 0xe8:
560ccd8014bSPhilippe Mathieu-Daudé             /*
561ccd8014bSPhilippe Mathieu-Daudé              * Mask writeblock size based on device width, or bank width if
5621997b485SRoy Franz              * device width not specified.
5631997b485SRoy Franz              */
5644dbda935SMarkus Armbruster             /* FIXME check @offset, @width */
5651997b485SRoy Franz             if (pfl->device_width) {
5661997b485SRoy Franz                 value = extract32(value, 0, pfl->device_width * 8);
5671997b485SRoy Franz             } else {
5681997b485SRoy Franz                 value = extract32(value, 0, pfl->bank_width * 8);
5691997b485SRoy Franz             }
57049ab747fSPaolo Bonzini             pfl->counter = value;
57149ab747fSPaolo Bonzini             pfl->wcycle++;
57249ab747fSPaolo Bonzini             break;
57349ab747fSPaolo Bonzini         case 0x60:
57449ab747fSPaolo Bonzini             if (cmd == 0xd0) {
57549ab747fSPaolo Bonzini                 pfl->wcycle = 0;
57649ab747fSPaolo Bonzini                 pfl->status |= 0x80;
57749ab747fSPaolo Bonzini             } else if (cmd == 0x01) {
57849ab747fSPaolo Bonzini                 pfl->wcycle = 0;
57949ab747fSPaolo Bonzini                 pfl->status |= 0x80;
5803072182dSPhilippe Mathieu-Daudé             } else if (cmd == 0xff) { /* Read Array */
5813072182dSPhilippe Mathieu-Daudé                 goto mode_read_array;
58249ab747fSPaolo Bonzini             } else {
58391316cbbSDavid Edmondson                 trace_pflash_write(pfl->name, "unknown (un)locking command");
5843072182dSPhilippe Mathieu-Daudé                 goto mode_read_array;
58549ab747fSPaolo Bonzini             }
58649ab747fSPaolo Bonzini             break;
58749ab747fSPaolo Bonzini         case 0x98:
5883072182dSPhilippe Mathieu-Daudé             if (cmd == 0xff) { /* Read Array */
5893072182dSPhilippe Mathieu-Daudé                 goto mode_read_array;
59049ab747fSPaolo Bonzini             } else {
59191316cbbSDavid Edmondson                 trace_pflash_write(pfl->name, "leaving query mode");
59249ab747fSPaolo Bonzini             }
59349ab747fSPaolo Bonzini             break;
59449ab747fSPaolo Bonzini         default:
59549ab747fSPaolo Bonzini             goto error_flash;
59649ab747fSPaolo Bonzini         }
59749ab747fSPaolo Bonzini         break;
59849ab747fSPaolo Bonzini     case 2:
59949ab747fSPaolo Bonzini         switch (pfl->cmd) {
60049ab747fSPaolo Bonzini         case 0xe8: /* Block write */
6014dbda935SMarkus Armbruster             /* FIXME check @offset, @width */
6022563be63SGerd Hoffmann             if (pfl->blk_offset == -1 && pfl->counter) {
6032563be63SGerd Hoffmann                 pflash_blk_write_start(pfl, offset);
6042563be63SGerd Hoffmann             }
605284a7ee2SGerd Hoffmann             if (!pfl->ro && (pfl->blk_offset != -1)) {
60649ab747fSPaolo Bonzini                 pflash_data_write(pfl, offset, value, width, be);
60749ab747fSPaolo Bonzini             } else {
60849ab747fSPaolo Bonzini                 pfl->status |= 0x10; /* Programming error */
60949ab747fSPaolo Bonzini             }
61049ab747fSPaolo Bonzini 
61149ab747fSPaolo Bonzini             pfl->status |= 0x80;
61249ab747fSPaolo Bonzini 
61349ab747fSPaolo Bonzini             if (!pfl->counter) {
61491316cbbSDavid Edmondson                 trace_pflash_write(pfl->name, "block write finished");
61549ab747fSPaolo Bonzini                 pfl->wcycle++;
6168f64e744SPeter Maydell                 break;
61749ab747fSPaolo Bonzini             }
61849ab747fSPaolo Bonzini 
61949ab747fSPaolo Bonzini             pfl->counter--;
62049ab747fSPaolo Bonzini             break;
62149ab747fSPaolo Bonzini         default:
62249ab747fSPaolo Bonzini             goto error_flash;
62349ab747fSPaolo Bonzini         }
62449ab747fSPaolo Bonzini         break;
62549ab747fSPaolo Bonzini     case 3: /* Confirm mode */
62649ab747fSPaolo Bonzini         switch (pfl->cmd) {
62749ab747fSPaolo Bonzini         case 0xe8: /* Block write */
628284a7ee2SGerd Hoffmann             if ((cmd == 0xd0) && !(pfl->status & 0x10)) {
629284a7ee2SGerd Hoffmann                 pflash_blk_write_flush(pfl);
63049ab747fSPaolo Bonzini                 pfl->wcycle = 0;
63149ab747fSPaolo Bonzini                 pfl->status |= 0x80;
63249ab747fSPaolo Bonzini             } else {
633284a7ee2SGerd Hoffmann                 pflash_blk_write_abort(pfl);
6343072182dSPhilippe Mathieu-Daudé                 goto mode_read_array;
63549ab747fSPaolo Bonzini             }
63649ab747fSPaolo Bonzini             break;
63749ab747fSPaolo Bonzini         default:
638284a7ee2SGerd Hoffmann             pflash_blk_write_abort(pfl);
63949ab747fSPaolo Bonzini             goto error_flash;
64049ab747fSPaolo Bonzini         }
64149ab747fSPaolo Bonzini         break;
64249ab747fSPaolo Bonzini     default:
64349ab747fSPaolo Bonzini         /* Should never happen */
64491316cbbSDavid Edmondson         trace_pflash_write(pfl->name, "invalid write state");
6453072182dSPhilippe Mathieu-Daudé         goto mode_read_array;
64649ab747fSPaolo Bonzini     }
64749ab747fSPaolo Bonzini     return;
64849ab747fSPaolo Bonzini 
64949ab747fSPaolo Bonzini  error_flash:
65049ab747fSPaolo Bonzini     qemu_log_mask(LOG_UNIMP, "%s: Unimplemented flash cmd sequence "
651883f2c59SPhilippe Mathieu-Daudé                   "(offset " HWADDR_FMT_plx ", wcycle 0x%x cmd 0x%x value 0x%x)"
65249ab747fSPaolo Bonzini                   "\n", __func__, offset, pfl->wcycle, pfl->cmd, value);
65349ab747fSPaolo Bonzini 
6543072182dSPhilippe Mathieu-Daudé  mode_read_array:
65591316cbbSDavid Edmondson     trace_pflash_mode_read_array(pfl->name);
6565f9a5ea1SJan Kiszka     memory_region_rom_device_set_romd(&pfl->mem, true);
65749ab747fSPaolo Bonzini     pfl->wcycle = 0;
658aba53a12SPhilippe Mathieu-Daudé     pfl->cmd = 0x00; /* This model reset value for READ_ARRAY (not CFI) */
65949ab747fSPaolo Bonzini }
66049ab747fSPaolo Bonzini 
66149ab747fSPaolo Bonzini 
pflash_mem_read_with_attrs(void * opaque,hwaddr addr,uint64_t * value,unsigned len,MemTxAttrs attrs)662f71e42a5SPaolo Bonzini static MemTxResult pflash_mem_read_with_attrs(void *opaque, hwaddr addr, uint64_t *value,
663f71e42a5SPaolo Bonzini                                               unsigned len, MemTxAttrs attrs)
66449ab747fSPaolo Bonzini {
66516434065SMarkus Armbruster     PFlashCFI01 *pfl = opaque;
6665aa113f0SPaolo Bonzini     bool be = !!(pfl->features & (1 << PFLASH_BE));
66749ab747fSPaolo Bonzini 
668f71e42a5SPaolo Bonzini     if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) {
669f71e42a5SPaolo Bonzini         *value = pflash_data_read(opaque, addr, len, be);
670f71e42a5SPaolo Bonzini     } else {
671f71e42a5SPaolo Bonzini         *value = pflash_read(opaque, addr, len, be);
672f71e42a5SPaolo Bonzini     }
673f71e42a5SPaolo Bonzini     return MEMTX_OK;
67449ab747fSPaolo Bonzini }
67549ab747fSPaolo Bonzini 
pflash_mem_write_with_attrs(void * opaque,hwaddr addr,uint64_t value,unsigned len,MemTxAttrs attrs)676f71e42a5SPaolo Bonzini static MemTxResult pflash_mem_write_with_attrs(void *opaque, hwaddr addr, uint64_t value,
677f71e42a5SPaolo Bonzini                                                unsigned len, MemTxAttrs attrs)
67849ab747fSPaolo Bonzini {
67916434065SMarkus Armbruster     PFlashCFI01 *pfl = opaque;
6805aa113f0SPaolo Bonzini     bool be = !!(pfl->features & (1 << PFLASH_BE));
68149ab747fSPaolo Bonzini 
682f71e42a5SPaolo Bonzini     if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) {
683f71e42a5SPaolo Bonzini         return MEMTX_ERROR;
684f71e42a5SPaolo Bonzini     } else {
685f71e42a5SPaolo Bonzini         pflash_write(opaque, addr, value, len, be);
686f71e42a5SPaolo Bonzini         return MEMTX_OK;
687f71e42a5SPaolo Bonzini     }
68849ab747fSPaolo Bonzini }
68949ab747fSPaolo Bonzini 
6905aa113f0SPaolo Bonzini static const MemoryRegionOps pflash_cfi01_ops = {
691f71e42a5SPaolo Bonzini     .read_with_attrs = pflash_mem_read_with_attrs,
692f71e42a5SPaolo Bonzini     .write_with_attrs = pflash_mem_write_with_attrs,
69349ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
69449ab747fSPaolo Bonzini };
69549ab747fSPaolo Bonzini 
pflash_cfi01_fill_cfi_table(PFlashCFI01 * pfl)696ef7716caSDaniel Henrique Barboza static void pflash_cfi01_fill_cfi_table(PFlashCFI01 *pfl)
69749ab747fSPaolo Bonzini {
698feb0b1aaSPeter Maydell     uint64_t blocks_per_device, sector_len_per_device, device_len;
699a0289b8aSPeter Maydell     int num_devices;
70049ab747fSPaolo Bonzini 
701ccd8014bSPhilippe Mathieu-Daudé     /*
702ccd8014bSPhilippe Mathieu-Daudé      * These are only used to expose the parameters of each device
703a0289b8aSPeter Maydell      * in the cfi_table[].
704a0289b8aSPeter Maydell      */
705a0289b8aSPeter Maydell     num_devices = pfl->device_width ? (pfl->bank_width / pfl->device_width) : 1;
706feb0b1aaSPeter Maydell     if (pfl->old_multiple_chip_handling) {
707a0289b8aSPeter Maydell         blocks_per_device = pfl->nb_blocs / num_devices;
708feb0b1aaSPeter Maydell         sector_len_per_device = pfl->sector_len;
709feb0b1aaSPeter Maydell     } else {
710feb0b1aaSPeter Maydell         blocks_per_device = pfl->nb_blocs;
711feb0b1aaSPeter Maydell         sector_len_per_device = pfl->sector_len / num_devices;
712feb0b1aaSPeter Maydell     }
713feb0b1aaSPeter Maydell     device_len = sector_len_per_device * blocks_per_device;
714a0289b8aSPeter Maydell 
71549ab747fSPaolo Bonzini     /* Hardcoded CFI table */
71649ab747fSPaolo Bonzini     /* Standard "QRY" string */
71749ab747fSPaolo Bonzini     pfl->cfi_table[0x10] = 'Q';
71849ab747fSPaolo Bonzini     pfl->cfi_table[0x11] = 'R';
71949ab747fSPaolo Bonzini     pfl->cfi_table[0x12] = 'Y';
72049ab747fSPaolo Bonzini     /* Command set (Intel) */
72149ab747fSPaolo Bonzini     pfl->cfi_table[0x13] = 0x01;
72249ab747fSPaolo Bonzini     pfl->cfi_table[0x14] = 0x00;
72349ab747fSPaolo Bonzini     /* Primary extended table address (none) */
72449ab747fSPaolo Bonzini     pfl->cfi_table[0x15] = 0x31;
72549ab747fSPaolo Bonzini     pfl->cfi_table[0x16] = 0x00;
72649ab747fSPaolo Bonzini     /* Alternate command set (none) */
72749ab747fSPaolo Bonzini     pfl->cfi_table[0x17] = 0x00;
72849ab747fSPaolo Bonzini     pfl->cfi_table[0x18] = 0x00;
72949ab747fSPaolo Bonzini     /* Alternate extended table (none) */
73049ab747fSPaolo Bonzini     pfl->cfi_table[0x19] = 0x00;
73149ab747fSPaolo Bonzini     pfl->cfi_table[0x1A] = 0x00;
73249ab747fSPaolo Bonzini     /* Vcc min */
73349ab747fSPaolo Bonzini     pfl->cfi_table[0x1B] = 0x45;
73449ab747fSPaolo Bonzini     /* Vcc max */
73549ab747fSPaolo Bonzini     pfl->cfi_table[0x1C] = 0x55;
73649ab747fSPaolo Bonzini     /* Vpp min (no Vpp pin) */
73749ab747fSPaolo Bonzini     pfl->cfi_table[0x1D] = 0x00;
73849ab747fSPaolo Bonzini     /* Vpp max (no Vpp pin) */
73949ab747fSPaolo Bonzini     pfl->cfi_table[0x1E] = 0x00;
74049ab747fSPaolo Bonzini     /* Reserved */
74149ab747fSPaolo Bonzini     pfl->cfi_table[0x1F] = 0x07;
74249ab747fSPaolo Bonzini     /* Timeout for min size buffer write */
74349ab747fSPaolo Bonzini     pfl->cfi_table[0x20] = 0x07;
74449ab747fSPaolo Bonzini     /* Typical timeout for block erase */
74549ab747fSPaolo Bonzini     pfl->cfi_table[0x21] = 0x0a;
74649ab747fSPaolo Bonzini     /* Typical timeout for full chip erase (4096 ms) */
74749ab747fSPaolo Bonzini     pfl->cfi_table[0x22] = 0x00;
74849ab747fSPaolo Bonzini     /* Reserved */
74949ab747fSPaolo Bonzini     pfl->cfi_table[0x23] = 0x04;
75049ab747fSPaolo Bonzini     /* Max timeout for buffer write */
75149ab747fSPaolo Bonzini     pfl->cfi_table[0x24] = 0x04;
75249ab747fSPaolo Bonzini     /* Max timeout for block erase */
75349ab747fSPaolo Bonzini     pfl->cfi_table[0x25] = 0x04;
75449ab747fSPaolo Bonzini     /* Max timeout for chip erase */
75549ab747fSPaolo Bonzini     pfl->cfi_table[0x26] = 0x00;
75649ab747fSPaolo Bonzini     /* Device size */
757a0289b8aSPeter Maydell     pfl->cfi_table[0x27] = ctz32(device_len); /* + 1; */
75849ab747fSPaolo Bonzini     /* Flash device interface (8 & 16 bits) */
75949ab747fSPaolo Bonzini     pfl->cfi_table[0x28] = 0x02;
76049ab747fSPaolo Bonzini     pfl->cfi_table[0x29] = 0x00;
76149ab747fSPaolo Bonzini     /* Max number of bytes in multi-bytes write */
7624b6fedcaSRoy Franz     if (pfl->bank_width == 1) {
76349ab747fSPaolo Bonzini         pfl->cfi_table[0x2A] = 0x08;
76449ab747fSPaolo Bonzini     } else {
76549ab747fSPaolo Bonzini         pfl->cfi_table[0x2A] = 0x0B;
76649ab747fSPaolo Bonzini     }
76749ab747fSPaolo Bonzini     pfl->writeblock_size = 1 << pfl->cfi_table[0x2A];
768feb0b1aaSPeter Maydell     if (!pfl->old_multiple_chip_handling && num_devices > 1) {
769feb0b1aaSPeter Maydell         pfl->writeblock_size *= num_devices;
770feb0b1aaSPeter Maydell     }
77149ab747fSPaolo Bonzini 
77249ab747fSPaolo Bonzini     pfl->cfi_table[0x2B] = 0x00;
77349ab747fSPaolo Bonzini     /* Number of erase block regions (uniform) */
77449ab747fSPaolo Bonzini     pfl->cfi_table[0x2C] = 0x01;
77549ab747fSPaolo Bonzini     /* Erase block region 1 */
776a0289b8aSPeter Maydell     pfl->cfi_table[0x2D] = blocks_per_device - 1;
777a0289b8aSPeter Maydell     pfl->cfi_table[0x2E] = (blocks_per_device - 1) >> 8;
778feb0b1aaSPeter Maydell     pfl->cfi_table[0x2F] = sector_len_per_device >> 8;
779feb0b1aaSPeter Maydell     pfl->cfi_table[0x30] = sector_len_per_device >> 16;
78049ab747fSPaolo Bonzini 
78149ab747fSPaolo Bonzini     /* Extended */
78249ab747fSPaolo Bonzini     pfl->cfi_table[0x31] = 'P';
78349ab747fSPaolo Bonzini     pfl->cfi_table[0x32] = 'R';
78449ab747fSPaolo Bonzini     pfl->cfi_table[0x33] = 'I';
78549ab747fSPaolo Bonzini 
78649ab747fSPaolo Bonzini     pfl->cfi_table[0x34] = '1';
78749ab747fSPaolo Bonzini     pfl->cfi_table[0x35] = '0';
78849ab747fSPaolo Bonzini 
78949ab747fSPaolo Bonzini     pfl->cfi_table[0x36] = 0x00;
79049ab747fSPaolo Bonzini     pfl->cfi_table[0x37] = 0x00;
79149ab747fSPaolo Bonzini     pfl->cfi_table[0x38] = 0x00;
79249ab747fSPaolo Bonzini     pfl->cfi_table[0x39] = 0x00;
79349ab747fSPaolo Bonzini 
79449ab747fSPaolo Bonzini     pfl->cfi_table[0x3a] = 0x00;
79549ab747fSPaolo Bonzini 
79649ab747fSPaolo Bonzini     pfl->cfi_table[0x3b] = 0x00;
79749ab747fSPaolo Bonzini     pfl->cfi_table[0x3c] = 0x00;
79849ab747fSPaolo Bonzini 
79949ab747fSPaolo Bonzini     pfl->cfi_table[0x3f] = 0x01; /* Number of protection fields */
80049ab747fSPaolo Bonzini }
80149ab747fSPaolo Bonzini 
pflash_cfi01_realize(DeviceState * dev,Error ** errp)802a42cd11bSPhilippe Mathieu-Daudé static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
803a42cd11bSPhilippe Mathieu-Daudé {
804a42cd11bSPhilippe Mathieu-Daudé     ERRP_GUARD();
805a42cd11bSPhilippe Mathieu-Daudé     PFlashCFI01 *pfl = PFLASH_CFI01(dev);
806a42cd11bSPhilippe Mathieu-Daudé     uint64_t total_len;
807a42cd11bSPhilippe Mathieu-Daudé     int ret;
808a42cd11bSPhilippe Mathieu-Daudé 
809a42cd11bSPhilippe Mathieu-Daudé     if (pfl->sector_len == 0) {
810a42cd11bSPhilippe Mathieu-Daudé         error_setg(errp, "attribute \"sector-length\" not specified or zero.");
811a42cd11bSPhilippe Mathieu-Daudé         return;
812a42cd11bSPhilippe Mathieu-Daudé     }
813a42cd11bSPhilippe Mathieu-Daudé     if (pfl->nb_blocs == 0) {
814a42cd11bSPhilippe Mathieu-Daudé         error_setg(errp, "attribute \"num-blocks\" not specified or zero.");
815a42cd11bSPhilippe Mathieu-Daudé         return;
816a42cd11bSPhilippe Mathieu-Daudé     }
817a42cd11bSPhilippe Mathieu-Daudé     if (pfl->name == NULL) {
818a42cd11bSPhilippe Mathieu-Daudé         error_setg(errp, "attribute \"name\" not specified.");
819a42cd11bSPhilippe Mathieu-Daudé         return;
820a42cd11bSPhilippe Mathieu-Daudé     }
821a42cd11bSPhilippe Mathieu-Daudé 
822a42cd11bSPhilippe Mathieu-Daudé     total_len = pfl->sector_len * pfl->nb_blocs;
823a42cd11bSPhilippe Mathieu-Daudé 
824a42cd11bSPhilippe Mathieu-Daudé     memory_region_init_rom_device(
825a42cd11bSPhilippe Mathieu-Daudé         &pfl->mem, OBJECT(dev),
826a42cd11bSPhilippe Mathieu-Daudé         &pflash_cfi01_ops,
827a42cd11bSPhilippe Mathieu-Daudé         pfl,
828a42cd11bSPhilippe Mathieu-Daudé         pfl->name, total_len, errp);
829a42cd11bSPhilippe Mathieu-Daudé     if (*errp) {
830a42cd11bSPhilippe Mathieu-Daudé         return;
831a42cd11bSPhilippe Mathieu-Daudé     }
832a42cd11bSPhilippe Mathieu-Daudé 
833a42cd11bSPhilippe Mathieu-Daudé     pfl->storage = memory_region_get_ram_ptr(&pfl->mem);
834a42cd11bSPhilippe Mathieu-Daudé     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem);
835a42cd11bSPhilippe Mathieu-Daudé 
836a42cd11bSPhilippe Mathieu-Daudé     if (pfl->blk) {
837a42cd11bSPhilippe Mathieu-Daudé         uint64_t perm;
838a42cd11bSPhilippe Mathieu-Daudé         pfl->ro = !blk_supports_write_perm(pfl->blk);
839a42cd11bSPhilippe Mathieu-Daudé         perm = BLK_PERM_CONSISTENT_READ | (pfl->ro ? 0 : BLK_PERM_WRITE);
840a42cd11bSPhilippe Mathieu-Daudé         ret = blk_set_perm(pfl->blk, perm, BLK_PERM_ALL, errp);
841a42cd11bSPhilippe Mathieu-Daudé         if (ret < 0) {
842a42cd11bSPhilippe Mathieu-Daudé             return;
843a42cd11bSPhilippe Mathieu-Daudé         }
844a42cd11bSPhilippe Mathieu-Daudé     } else {
8452231bee2SDavid Edmondson         pfl->ro = false;
846a42cd11bSPhilippe Mathieu-Daudé     }
847a42cd11bSPhilippe Mathieu-Daudé 
848a42cd11bSPhilippe Mathieu-Daudé     if (pfl->blk) {
849954b33daSManos Pitsidianakis         if (!blk_check_size_and_read_all(pfl->blk, dev, pfl->storage,
850954b33daSManos Pitsidianakis                                          total_len, errp)) {
851a42cd11bSPhilippe Mathieu-Daudé             vmstate_unregister_ram(&pfl->mem, DEVICE(pfl));
852a42cd11bSPhilippe Mathieu-Daudé             return;
853a42cd11bSPhilippe Mathieu-Daudé         }
854a42cd11bSPhilippe Mathieu-Daudé     }
855a42cd11bSPhilippe Mathieu-Daudé 
856a42cd11bSPhilippe Mathieu-Daudé     /*
857a42cd11bSPhilippe Mathieu-Daudé      * Default to devices being used at their maximum device width. This was
858a42cd11bSPhilippe Mathieu-Daudé      * assumed before the device_width support was added.
859a42cd11bSPhilippe Mathieu-Daudé      */
860a42cd11bSPhilippe Mathieu-Daudé     if (!pfl->max_device_width) {
861a42cd11bSPhilippe Mathieu-Daudé         pfl->max_device_width = pfl->device_width;
862a42cd11bSPhilippe Mathieu-Daudé     }
863a42cd11bSPhilippe Mathieu-Daudé 
864a42cd11bSPhilippe Mathieu-Daudé     pfl->wcycle = 0;
865a42cd11bSPhilippe Mathieu-Daudé     /*
866a42cd11bSPhilippe Mathieu-Daudé      * The command 0x00 is not assigned by the CFI open standard,
867a42cd11bSPhilippe Mathieu-Daudé      * but QEMU historically uses it for the READ_ARRAY command (0xff).
868a42cd11bSPhilippe Mathieu-Daudé      */
869a42cd11bSPhilippe Mathieu-Daudé     pfl->cmd = 0x00;
870a42cd11bSPhilippe Mathieu-Daudé     pfl->status = 0x80; /* WSM ready */
871ef7716caSDaniel Henrique Barboza     pflash_cfi01_fill_cfi_table(pfl);
872284a7ee2SGerd Hoffmann 
873284a7ee2SGerd Hoffmann     pfl->blk_bytes = g_malloc(pfl->writeblock_size);
874284a7ee2SGerd Hoffmann     pfl->blk_offset = -1;
875a42cd11bSPhilippe Mathieu-Daudé }
876a42cd11bSPhilippe Mathieu-Daudé 
pflash_cfi01_system_reset(DeviceState * dev)8773a283507SPhilippe Mathieu-Daudé static void pflash_cfi01_system_reset(DeviceState *dev)
8783a283507SPhilippe Mathieu-Daudé {
8793a283507SPhilippe Mathieu-Daudé     PFlashCFI01 *pfl = PFLASH_CFI01(dev);
8803a283507SPhilippe Mathieu-Daudé 
88191316cbbSDavid Edmondson     trace_pflash_reset(pfl->name);
8823a283507SPhilippe Mathieu-Daudé     /*
8833a283507SPhilippe Mathieu-Daudé      * The command 0x00 is not assigned by the CFI open standard,
8843a283507SPhilippe Mathieu-Daudé      * but QEMU historically uses it for the READ_ARRAY command (0xff).
8853a283507SPhilippe Mathieu-Daudé      */
8863a283507SPhilippe Mathieu-Daudé     pfl->cmd = 0x00;
8873a283507SPhilippe Mathieu-Daudé     pfl->wcycle = 0;
8883a283507SPhilippe Mathieu-Daudé     memory_region_rom_device_set_romd(&pfl->mem, true);
8893a283507SPhilippe Mathieu-Daudé     /*
8903a283507SPhilippe Mathieu-Daudé      * The WSM ready timer occurs at most 150ns after system reset.
8913a283507SPhilippe Mathieu-Daudé      * This model deliberately ignores this delay.
8923a283507SPhilippe Mathieu-Daudé      */
8933a283507SPhilippe Mathieu-Daudé     pfl->status = 0x80;
894284a7ee2SGerd Hoffmann 
895284a7ee2SGerd Hoffmann     pfl->blk_offset = -1;
8963a283507SPhilippe Mathieu-Daudé }
8973a283507SPhilippe Mathieu-Daudé 
89849ab747fSPaolo Bonzini static Property pflash_cfi01_properties[] = {
89916434065SMarkus Armbruster     DEFINE_PROP_DRIVE("drive", PFlashCFI01, blk),
900a0289b8aSPeter Maydell     /* num-blocks is the number of blocks actually visible to the guest,
901a0289b8aSPeter Maydell      * ie the total size of the device divided by the sector length.
902a0289b8aSPeter Maydell      * If we're emulating flash devices wired in parallel the actual
9039b4b4e51SMichael Tokarev      * number of blocks per individual device will differ.
904a0289b8aSPeter Maydell      */
90516434065SMarkus Armbruster     DEFINE_PROP_UINT32("num-blocks", PFlashCFI01, nb_blocs, 0),
90616434065SMarkus Armbruster     DEFINE_PROP_UINT64("sector-length", PFlashCFI01, sector_len, 0),
907fa21a7b1SRoy Franz     /* width here is the overall width of this QEMU device in bytes.
908fa21a7b1SRoy Franz      * The QEMU device may be emulating a number of flash devices
909fa21a7b1SRoy Franz      * wired up in parallel; the width of each individual flash
910fa21a7b1SRoy Franz      * device should be specified via device-width. If the individual
911fa21a7b1SRoy Franz      * devices have a maximum width which is greater than the width
912fa21a7b1SRoy Franz      * they are being used for, this maximum width should be set via
913fa21a7b1SRoy Franz      * max-device-width (which otherwise defaults to device-width).
914fa21a7b1SRoy Franz      * So for instance a 32-bit wide QEMU flash device made from four
915fa21a7b1SRoy Franz      * 16-bit flash devices used in 8-bit wide mode would be configured
916fa21a7b1SRoy Franz      * with width = 4, device-width = 1, max-device-width = 2.
917fa21a7b1SRoy Franz      *
918fa21a7b1SRoy Franz      * If device-width is not specified we default to backwards
919fa21a7b1SRoy Franz      * compatible behaviour which is a bad emulation of two
920fa21a7b1SRoy Franz      * 16 bit devices making up a 32 bit wide QEMU device. This
921fa21a7b1SRoy Franz      * is deprecated for new uses of this device.
922fa21a7b1SRoy Franz      */
92316434065SMarkus Armbruster     DEFINE_PROP_UINT8("width", PFlashCFI01, bank_width, 0),
92416434065SMarkus Armbruster     DEFINE_PROP_UINT8("device-width", PFlashCFI01, device_width, 0),
92516434065SMarkus Armbruster     DEFINE_PROP_UINT8("max-device-width", PFlashCFI01, max_device_width, 0),
92616434065SMarkus Armbruster     DEFINE_PROP_BIT("big-endian", PFlashCFI01, features, PFLASH_BE, 0),
92716434065SMarkus Armbruster     DEFINE_PROP_BIT("secure", PFlashCFI01, features, PFLASH_SECURE, 0),
92816434065SMarkus Armbruster     DEFINE_PROP_UINT16("id0", PFlashCFI01, ident0, 0),
92916434065SMarkus Armbruster     DEFINE_PROP_UINT16("id1", PFlashCFI01, ident1, 0),
93016434065SMarkus Armbruster     DEFINE_PROP_UINT16("id2", PFlashCFI01, ident2, 0),
93116434065SMarkus Armbruster     DEFINE_PROP_UINT16("id3", PFlashCFI01, ident3, 0),
93216434065SMarkus Armbruster     DEFINE_PROP_STRING("name", PFlashCFI01, name),
93316434065SMarkus Armbruster     DEFINE_PROP_BOOL("old-multiple-chip-handling", PFlashCFI01,
934feb0b1aaSPeter Maydell                      old_multiple_chip_handling, false),
93549ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
93649ab747fSPaolo Bonzini };
93749ab747fSPaolo Bonzini 
pflash_cfi01_class_init(ObjectClass * klass,void * data)93849ab747fSPaolo Bonzini static void pflash_cfi01_class_init(ObjectClass *klass, void *data)
93949ab747fSPaolo Bonzini {
94049ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
94149ab747fSPaolo Bonzini 
942e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, pflash_cfi01_system_reset);
943e40b5f3eSHu Tao     dc->realize = pflash_cfi01_realize;
9444f67d30bSMarc-André Lureau     device_class_set_props(dc, pflash_cfi01_properties);
94549ab747fSPaolo Bonzini     dc->vmsd = &vmstate_pflash;
946125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
94749ab747fSPaolo Bonzini }
94849ab747fSPaolo Bonzini 
949*2b88cd17SBernhard Beschow static const TypeInfo pflash_cfi01_types[] = {
950*2b88cd17SBernhard Beschow     {
951e7b62741SMarkus Armbruster         .name           = TYPE_PFLASH_CFI01,
95249ab747fSPaolo Bonzini         .parent         = TYPE_SYS_BUS_DEVICE,
95316434065SMarkus Armbruster         .instance_size  = sizeof(PFlashCFI01),
95449ab747fSPaolo Bonzini         .class_init     = pflash_cfi01_class_init,
955*2b88cd17SBernhard Beschow     },
95649ab747fSPaolo Bonzini };
95749ab747fSPaolo Bonzini 
DEFINE_TYPES(pflash_cfi01_types)958*2b88cd17SBernhard Beschow DEFINE_TYPES(pflash_cfi01_types)
95949ab747fSPaolo Bonzini 
96016434065SMarkus Armbruster PFlashCFI01 *pflash_cfi01_register(hwaddr base,
961940d5b13SMarkus Armbruster                                    const char *name,
96249ab747fSPaolo Bonzini                                    hwaddr size,
9634be74634SMarkus Armbruster                                    BlockBackend *blk,
964ce14710fSMarkus Armbruster                                    uint32_t sector_len,
96516434065SMarkus Armbruster                                    int bank_width,
96616434065SMarkus Armbruster                                    uint16_t id0, uint16_t id1,
96716434065SMarkus Armbruster                                    uint16_t id2, uint16_t id3,
96816434065SMarkus Armbruster                                    int be)
96949ab747fSPaolo Bonzini {
9703e80f690SMarkus Armbruster     DeviceState *dev = qdev_new(TYPE_PFLASH_CFI01);
97149ab747fSPaolo Bonzini 
9729b3d111aSMarkus Armbruster     if (blk) {
973934df912SMarkus Armbruster         qdev_prop_set_drive(dev, "drive", blk);
97449ab747fSPaolo Bonzini     }
9754cdd0a77SPhilippe Mathieu-Daudé     assert(QEMU_IS_ALIGNED(size, sector_len));
976ce14710fSMarkus Armbruster     qdev_prop_set_uint32(dev, "num-blocks", size / sector_len);
97749ab747fSPaolo Bonzini     qdev_prop_set_uint64(dev, "sector-length", sector_len);
9784b6fedcaSRoy Franz     qdev_prop_set_uint8(dev, "width", bank_width);
979e9809422SPaolo Bonzini     qdev_prop_set_bit(dev, "big-endian", !!be);
98049ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id0", id0);
98149ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id1", id1);
98249ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id2", id2);
98349ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id3", id3);
98449ab747fSPaolo Bonzini     qdev_prop_set_string(dev, "name", name);
9853c6ef471SMarkus Armbruster     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
98649ab747fSPaolo Bonzini 
987f1b44f0eSHu Tao     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
988e7b62741SMarkus Armbruster     return PFLASH_CFI01(dev);
98949ab747fSPaolo Bonzini }
99049ab747fSPaolo Bonzini 
pflash_cfi01_get_blk(PFlashCFI01 * fl)991e60cf765SPhilippe Mathieu-Daudé BlockBackend *pflash_cfi01_get_blk(PFlashCFI01 *fl)
992e60cf765SPhilippe Mathieu-Daudé {
993e60cf765SPhilippe Mathieu-Daudé     return fl->blk;
994e60cf765SPhilippe Mathieu-Daudé }
995e60cf765SPhilippe Mathieu-Daudé 
pflash_cfi01_get_memory(PFlashCFI01 * fl)99616434065SMarkus Armbruster MemoryRegion *pflash_cfi01_get_memory(PFlashCFI01 *fl)
99749ab747fSPaolo Bonzini {
99849ab747fSPaolo Bonzini     return &fl->mem;
99949ab747fSPaolo Bonzini }
10004c0cfc72SLaszlo Ersek 
10012d731dbdSMarkus Armbruster /*
10022d731dbdSMarkus Armbruster  * Handle -drive if=pflash for machines that use properties.
10032d731dbdSMarkus Armbruster  * If @dinfo is null, do nothing.
10042d731dbdSMarkus Armbruster  * Else if @fl's property "drive" is already set, fatal error.
10052d731dbdSMarkus Armbruster  * Else set it to the BlockBackend with @dinfo.
10062d731dbdSMarkus Armbruster  */
pflash_cfi01_legacy_drive(PFlashCFI01 * fl,DriveInfo * dinfo)10072d731dbdSMarkus Armbruster void pflash_cfi01_legacy_drive(PFlashCFI01 *fl, DriveInfo *dinfo)
10082d731dbdSMarkus Armbruster {
10092d731dbdSMarkus Armbruster     Location loc;
10102d731dbdSMarkus Armbruster 
10112d731dbdSMarkus Armbruster     if (!dinfo) {
10122d731dbdSMarkus Armbruster         return;
10132d731dbdSMarkus Armbruster     }
10142d731dbdSMarkus Armbruster 
10152d731dbdSMarkus Armbruster     loc_push_none(&loc);
10162d731dbdSMarkus Armbruster     qemu_opts_loc_restore(dinfo->opts);
10172d731dbdSMarkus Armbruster     if (fl->blk) {
10182d731dbdSMarkus Armbruster         error_report("clashes with -machine");
10192d731dbdSMarkus Armbruster         exit(1);
10202d731dbdSMarkus Armbruster     }
1021934df912SMarkus Armbruster     qdev_prop_set_drive_err(DEVICE(fl), "drive", blk_by_legacy_dinfo(dinfo),
1022934df912SMarkus Armbruster                             &error_fatal);
10232d731dbdSMarkus Armbruster     loc_pop(&loc);
10242d731dbdSMarkus Armbruster }
10252d731dbdSMarkus Armbruster 
postload_update_cb(void * opaque,bool running,RunState state)1026538f0497SPhilippe Mathieu-Daudé static void postload_update_cb(void *opaque, bool running, RunState state)
102790c647dbSDr. David Alan Gilbert {
102816434065SMarkus Armbruster     PFlashCFI01 *pfl = opaque;
102990c647dbSDr. David Alan Gilbert 
10303b717194SEmanuele Giuseppe Esposito     /* This is called after bdrv_activate_all.  */
103190c647dbSDr. David Alan Gilbert     qemu_del_vm_change_state_handler(pfl->vmstate);
103290c647dbSDr. David Alan Gilbert     pfl->vmstate = NULL;
103390c647dbSDr. David Alan Gilbert 
103491316cbbSDavid Edmondson     trace_pflash_postload_cb(pfl->name);
103590c647dbSDr. David Alan Gilbert     pflash_update(pfl, 0, pfl->sector_len * pfl->nb_blocs);
103690c647dbSDr. David Alan Gilbert }
103790c647dbSDr. David Alan Gilbert 
pflash_post_load(void * opaque,int version_id)10384c0cfc72SLaszlo Ersek static int pflash_post_load(void *opaque, int version_id)
10394c0cfc72SLaszlo Ersek {
104016434065SMarkus Armbruster     PFlashCFI01 *pfl = opaque;
10414c0cfc72SLaszlo Ersek 
10424c0cfc72SLaszlo Ersek     if (!pfl->ro) {
104390c647dbSDr. David Alan Gilbert         pfl->vmstate = qemu_add_vm_change_state_handler(postload_update_cb,
104490c647dbSDr. David Alan Gilbert                                                         pfl);
10454c0cfc72SLaszlo Ersek     }
10464c0cfc72SLaszlo Ersek     return 0;
10474c0cfc72SLaszlo Ersek }
1048