xref: /openbmc/qemu/hw/block/m25p80.c (revision 71df78a3)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * ST M25P80 emulator. Emulate all SPI flash devices based on the m25p80 command
349ab747fSPaolo Bonzini  * set. Known devices table current as of Jun/2012 and taken from linux.
449ab747fSPaolo Bonzini  * See drivers/mtd/devices/m25p80.c.
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * Copyright (C) 2011 Edgar E. Iglesias <edgar.iglesias@gmail.com>
749ab747fSPaolo Bonzini  * Copyright (C) 2012 Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
849ab747fSPaolo Bonzini  * Copyright (C) 2012 PetaLogix
949ab747fSPaolo Bonzini  *
1049ab747fSPaolo Bonzini  * This program is free software; you can redistribute it and/or
1149ab747fSPaolo Bonzini  * modify it under the terms of the GNU General Public License as
1249ab747fSPaolo Bonzini  * published by the Free Software Foundation; either version 2 or
1349ab747fSPaolo Bonzini  * (at your option) a later version of the License.
1449ab747fSPaolo Bonzini  *
1549ab747fSPaolo Bonzini  * This program is distributed in the hope that it will be useful,
1649ab747fSPaolo Bonzini  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1749ab747fSPaolo Bonzini  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1849ab747fSPaolo Bonzini  * GNU General Public License for more details.
1949ab747fSPaolo Bonzini  *
2049ab747fSPaolo Bonzini  * You should have received a copy of the GNU General Public License along
2149ab747fSPaolo Bonzini  * with this program; if not, see <http://www.gnu.org/licenses/>.
2249ab747fSPaolo Bonzini  */
2349ab747fSPaolo Bonzini 
2480c71a24SPeter Maydell #include "qemu/osdep.h"
25e8400cf3SPhilippe Mathieu-Daudé #include "qemu/units.h"
26fa1d36dfSMarkus Armbruster #include "sysemu/block-backend.h"
2711aeb4b8SCédric Le Goater #include "hw/block/block.h"
28*71df78a3SCédric Le Goater #include "hw/block/flash.h"
29a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
30ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
318fd06719SAlistair Francis #include "hw/ssi/ssi.h"
32d6454270SMarkus Armbruster #include "migration/vmstate.h"
33cb475951SMarcin Krzeminski #include "qemu/bitops.h"
3403dd024fSPaolo Bonzini #include "qemu/log.h"
350b8fa32fSMarkus Armbruster #include "qemu/module.h"
3624cb2e0dSJean-Christophe Dubois #include "qemu/error-report.h"
377673bb4cSCédric Le Goater #include "qapi/error.h"
38ccc46090SGuenter Roeck #include "trace.h"
39db1015e9SEduardo Habkost #include "qom/object.h"
402389bcc2SCédric Le Goater #include "m25p80_sfdp.h"
4149ab747fSPaolo Bonzini 
42d8a29a7aSMarcin Krzeminski /* 16 MiB max in 3 byte address mode */
43d8a29a7aSMarcin Krzeminski #define MAX_3BYTES_SIZE 0x1000000
44e3ba6cd6SMarcin Krzeminski #define SPI_NOR_MAX_ID_LEN 6
45e3ba6cd6SMarcin Krzeminski 
462113a128SIris Chen /* Fields for FlashPartInfo->flags */
472113a128SIris Chen enum spi_flash_option_flags {
482113a128SIris Chen     ER_4K                  = BIT(0),
492113a128SIris Chen     ER_32K                 = BIT(1),
502113a128SIris Chen     EEPROM                 = BIT(2),
512113a128SIris Chen     HAS_SR_TB              = BIT(3),
522113a128SIris Chen     HAS_SR_BP3_BIT6        = BIT(4),
532113a128SIris Chen };
542113a128SIris Chen 
5549ab747fSPaolo Bonzini typedef struct FlashPartInfo {
5649ab747fSPaolo Bonzini     const char *part_name;
57e3ba6cd6SMarcin Krzeminski     /*
58e3ba6cd6SMarcin Krzeminski      * This array stores the ID bytes.
59e3ba6cd6SMarcin Krzeminski      * The first three bytes are the JEDIC ID.
60e3ba6cd6SMarcin Krzeminski      * JEDEC ID zero means "no ID" (mostly older chips).
61e3ba6cd6SMarcin Krzeminski      */
62e3ba6cd6SMarcin Krzeminski     uint8_t id[SPI_NOR_MAX_ID_LEN];
63e3ba6cd6SMarcin Krzeminski     uint8_t id_len;
6449ab747fSPaolo Bonzini     /* there is confusion between manufacturers as to what a sector is. In this
6549ab747fSPaolo Bonzini      * device model, a "sector" is the size that is erased by the ERASE_SECTOR
6649ab747fSPaolo Bonzini      * command (opcode 0xd8).
6749ab747fSPaolo Bonzini      */
6849ab747fSPaolo Bonzini     uint32_t sector_size;
6949ab747fSPaolo Bonzini     uint32_t n_sectors;
7049ab747fSPaolo Bonzini     uint32_t page_size;
7176e87269SMarcin Krzeminski     uint16_t flags;
72f509dfeeSMarcin Krzeminski     /*
73f509dfeeSMarcin Krzeminski      * Big sized spi nor are often stacked devices, thus sometime
74f509dfeeSMarcin Krzeminski      * replace chip erase with die erase.
75f509dfeeSMarcin Krzeminski      * This field inform how many die is in the chip.
76f509dfeeSMarcin Krzeminski      */
77f509dfeeSMarcin Krzeminski     uint8_t die_cnt;
782389bcc2SCédric Le Goater     uint8_t (*sfdp_read)(uint32_t sfdp_addr);
7949ab747fSPaolo Bonzini } FlashPartInfo;
8049ab747fSPaolo Bonzini 
8149ab747fSPaolo Bonzini /* adapted from linux */
82e3ba6cd6SMarcin Krzeminski /* Used when the "_ext_id" is two bytes at most */
83e3ba6cd6SMarcin Krzeminski #define INFO(_part_name, _jedec_id, _ext_id, _sector_size, _n_sectors, _flags)\
84e3ba6cd6SMarcin Krzeminski     .part_name = _part_name,\
85e3ba6cd6SMarcin Krzeminski     .id = {\
86e3ba6cd6SMarcin Krzeminski         ((_jedec_id) >> 16) & 0xff,\
87e3ba6cd6SMarcin Krzeminski         ((_jedec_id) >> 8) & 0xff,\
88e3ba6cd6SMarcin Krzeminski         (_jedec_id) & 0xff,\
89e3ba6cd6SMarcin Krzeminski         ((_ext_id) >> 8) & 0xff,\
90e3ba6cd6SMarcin Krzeminski         (_ext_id) & 0xff,\
91e3ba6cd6SMarcin Krzeminski           },\
92e3ba6cd6SMarcin Krzeminski     .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),\
93e3ba6cd6SMarcin Krzeminski     .sector_size = (_sector_size),\
94e3ba6cd6SMarcin Krzeminski     .n_sectors = (_n_sectors),\
95e3ba6cd6SMarcin Krzeminski     .page_size = 256,\
96f509dfeeSMarcin Krzeminski     .flags = (_flags),\
97f509dfeeSMarcin Krzeminski     .die_cnt = 0
9849ab747fSPaolo Bonzini 
99e3ba6cd6SMarcin Krzeminski #define INFO6(_part_name, _jedec_id, _ext_id, _sector_size, _n_sectors, _flags)\
100e3ba6cd6SMarcin Krzeminski     .part_name = _part_name,\
101e3ba6cd6SMarcin Krzeminski     .id = {\
102e3ba6cd6SMarcin Krzeminski         ((_jedec_id) >> 16) & 0xff,\
103e3ba6cd6SMarcin Krzeminski         ((_jedec_id) >> 8) & 0xff,\
104e3ba6cd6SMarcin Krzeminski         (_jedec_id) & 0xff,\
105e3ba6cd6SMarcin Krzeminski         ((_ext_id) >> 16) & 0xff,\
106e3ba6cd6SMarcin Krzeminski         ((_ext_id) >> 8) & 0xff,\
107e3ba6cd6SMarcin Krzeminski         (_ext_id) & 0xff,\
108e3ba6cd6SMarcin Krzeminski           },\
109e3ba6cd6SMarcin Krzeminski     .id_len = 6,\
11049ab747fSPaolo Bonzini     .sector_size = (_sector_size),\
11149ab747fSPaolo Bonzini     .n_sectors = (_n_sectors),\
11249ab747fSPaolo Bonzini     .page_size = 256,\
11349ab747fSPaolo Bonzini     .flags = (_flags),\
114f509dfeeSMarcin Krzeminski     .die_cnt = 0
115f509dfeeSMarcin Krzeminski 
116f509dfeeSMarcin Krzeminski #define INFO_STACKED(_part_name, _jedec_id, _ext_id, _sector_size, _n_sectors,\
117f509dfeeSMarcin Krzeminski                     _flags, _die_cnt)\
118f509dfeeSMarcin Krzeminski     .part_name = _part_name,\
119f509dfeeSMarcin Krzeminski     .id = {\
120f509dfeeSMarcin Krzeminski         ((_jedec_id) >> 16) & 0xff,\
121f509dfeeSMarcin Krzeminski         ((_jedec_id) >> 8) & 0xff,\
122f509dfeeSMarcin Krzeminski         (_jedec_id) & 0xff,\
123f509dfeeSMarcin Krzeminski         ((_ext_id) >> 8) & 0xff,\
124f509dfeeSMarcin Krzeminski         (_ext_id) & 0xff,\
125f509dfeeSMarcin Krzeminski           },\
126f509dfeeSMarcin Krzeminski     .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),\
127f509dfeeSMarcin Krzeminski     .sector_size = (_sector_size),\
128f509dfeeSMarcin Krzeminski     .n_sectors = (_n_sectors),\
129f509dfeeSMarcin Krzeminski     .page_size = 256,\
130f509dfeeSMarcin Krzeminski     .flags = (_flags),\
131f509dfeeSMarcin Krzeminski     .die_cnt = _die_cnt
13249ab747fSPaolo Bonzini 
13349ab747fSPaolo Bonzini #define JEDEC_NUMONYX 0x20
13449ab747fSPaolo Bonzini #define JEDEC_WINBOND 0xEF
13549ab747fSPaolo Bonzini #define JEDEC_SPANSION 0x01
13649ab747fSPaolo Bonzini 
137cb475951SMarcin Krzeminski /* Numonyx (Micron) Configuration register macros */
138cb475951SMarcin Krzeminski #define VCFG_DUMMY 0x1
139cb475951SMarcin Krzeminski #define VCFG_WRAP_SEQUENTIAL 0x2
140cb475951SMarcin Krzeminski #define NVCFG_XIP_MODE_DISABLED (7 << 9)
141cb475951SMarcin Krzeminski #define NVCFG_XIP_MODE_MASK (7 << 9)
14209414144SJoe Komlodi #define VCFG_XIP_MODE_DISABLED (1 << 3)
143cb475951SMarcin Krzeminski #define CFG_DUMMY_CLK_LEN 4
144cb475951SMarcin Krzeminski #define NVCFG_DUMMY_CLK_POS 12
145cb475951SMarcin Krzeminski #define VCFG_DUMMY_CLK_POS 4
1465c765e7aSStefan Weil #define EVCFG_OUT_DRIVER_STRENGTH_DEF 7
147cb475951SMarcin Krzeminski #define EVCFG_VPP_ACCELERATOR (1 << 3)
148cb475951SMarcin Krzeminski #define EVCFG_RESET_HOLD_ENABLED (1 << 4)
149cb475951SMarcin Krzeminski #define NVCFG_DUAL_IO_MASK (1 << 2)
15009414144SJoe Komlodi #define EVCFG_DUAL_IO_DISABLED (1 << 6)
151cb475951SMarcin Krzeminski #define NVCFG_QUAD_IO_MASK (1 << 3)
15209414144SJoe Komlodi #define EVCFG_QUAD_IO_DISABLED (1 << 7)
153cb475951SMarcin Krzeminski #define NVCFG_4BYTE_ADDR_MASK (1 << 0)
154cb475951SMarcin Krzeminski #define NVCFG_LOWER_SEGMENT_MASK (1 << 1)
155cb475951SMarcin Krzeminski 
1569fbaa364SMarcin Krzeminski /* Numonyx (Micron) Flag Status Register macros */
1579fbaa364SMarcin Krzeminski #define FSR_4BYTE_ADDR_MODE_ENABLED 0x1
1589fbaa364SMarcin Krzeminski #define FSR_FLASH_READY (1 << 7)
1599fbaa364SMarcin Krzeminski 
160d9cc8701SMarcin Krzeminski /* Spansion configuration registers macros. */
161d9cc8701SMarcin Krzeminski #define SPANSION_QUAD_CFG_POS 0
162d9cc8701SMarcin Krzeminski #define SPANSION_QUAD_CFG_LEN 1
163d9cc8701SMarcin Krzeminski #define SPANSION_DUMMY_CLK_POS 0
164d9cc8701SMarcin Krzeminski #define SPANSION_DUMMY_CLK_LEN 4
165d9cc8701SMarcin Krzeminski #define SPANSION_ADDR_LEN_POS 7
166d9cc8701SMarcin Krzeminski #define SPANSION_ADDR_LEN_LEN 1
167d9cc8701SMarcin Krzeminski 
168cf6f1efeSMarcin Krzeminski /*
169cf6f1efeSMarcin Krzeminski  * Spansion read mode command length in bytes,
170cf6f1efeSMarcin Krzeminski  * the mode is currently not supported.
171cf6f1efeSMarcin Krzeminski */
172cf6f1efeSMarcin Krzeminski 
173cf6f1efeSMarcin Krzeminski #define SPANSION_CONTINUOUS_READ_MODE_CMD_LEN 1
174fe847705SMarcin Krzeminski #define WINBOND_CONTINUOUS_READ_MODE_CMD_LEN 1
175cf6f1efeSMarcin Krzeminski 
17649ab747fSPaolo Bonzini static const FlashPartInfo known_devices[] = {
17749ab747fSPaolo Bonzini     /* Atmel -- some are (confusingly) marketed as "DataFlash" */
17849ab747fSPaolo Bonzini     { INFO("at25fs010",   0x1f6601,      0,  32 << 10,   4, ER_4K) },
17949ab747fSPaolo Bonzini     { INFO("at25fs040",   0x1f6604,      0,  64 << 10,   8, ER_4K) },
18049ab747fSPaolo Bonzini 
18149ab747fSPaolo Bonzini     { INFO("at25df041a",  0x1f4401,      0,  64 << 10,   8, ER_4K) },
18249ab747fSPaolo Bonzini     { INFO("at25df321a",  0x1f4701,      0,  64 << 10,  64, ER_4K) },
18349ab747fSPaolo Bonzini     { INFO("at25df641",   0x1f4800,      0,  64 << 10, 128, ER_4K) },
18449ab747fSPaolo Bonzini 
18549ab747fSPaolo Bonzini     { INFO("at26f004",    0x1f0400,      0,  64 << 10,   8, ER_4K) },
18649ab747fSPaolo Bonzini     { INFO("at26df081a",  0x1f4501,      0,  64 << 10,  16, ER_4K) },
18749ab747fSPaolo Bonzini     { INFO("at26df161a",  0x1f4601,      0,  64 << 10,  32, ER_4K) },
18849ab747fSPaolo Bonzini     { INFO("at26df321",   0x1f4700,      0,  64 << 10,  64, ER_4K) },
18949ab747fSPaolo Bonzini 
1903e758c1dSEd Maste     { INFO("at45db081d",  0x1f2500,      0,  64 << 10,  16, ER_4K) },
1913e758c1dSEd Maste 
1921435bcd6SMarcin Krzeminski     /* Atmel EEPROMS - it is assumed, that don't care bit in command
1931435bcd6SMarcin Krzeminski      * is set to 0. Block protection is not supported.
1941435bcd6SMarcin Krzeminski      */
1951435bcd6SMarcin Krzeminski     { INFO("at25128a-nonjedec", 0x0,     0,         1, 131072, EEPROM) },
1961435bcd6SMarcin Krzeminski     { INFO("at25256a-nonjedec", 0x0,     0,         1, 262144, EEPROM) },
1971435bcd6SMarcin Krzeminski 
19849ab747fSPaolo Bonzini     /* EON -- en25xxx */
19949ab747fSPaolo Bonzini     { INFO("en25f32",     0x1c3116,      0,  64 << 10,  64, ER_4K) },
20049ab747fSPaolo Bonzini     { INFO("en25p32",     0x1c2016,      0,  64 << 10,  64, 0) },
20149ab747fSPaolo Bonzini     { INFO("en25q32b",    0x1c3016,      0,  64 << 10,  64, 0) },
20249ab747fSPaolo Bonzini     { INFO("en25p64",     0x1c2017,      0,  64 << 10, 128, 0) },
2033e758c1dSEd Maste     { INFO("en25q64",     0x1c3017,      0,  64 << 10, 128, ER_4K) },
2043e758c1dSEd Maste 
2053e758c1dSEd Maste     /* GigaDevice */
2063e758c1dSEd Maste     { INFO("gd25q32",     0xc84016,      0,  64 << 10,  64, ER_4K) },
2073e758c1dSEd Maste     { INFO("gd25q64",     0xc84017,      0,  64 << 10, 128, ER_4K) },
20849ab747fSPaolo Bonzini 
20949ab747fSPaolo Bonzini     /* Intel/Numonyx -- xxxs33b */
21049ab747fSPaolo Bonzini     { INFO("160s33b",     0x898911,      0,  64 << 10,  32, 0) },
21149ab747fSPaolo Bonzini     { INFO("320s33b",     0x898912,      0,  64 << 10,  64, 0) },
21249ab747fSPaolo Bonzini     { INFO("640s33b",     0x898913,      0,  64 << 10, 128, 0) },
2133e758c1dSEd Maste     { INFO("n25q064",     0x20ba17,      0,  64 << 10, 128, 0) },
21449ab747fSPaolo Bonzini 
21562d10766SBin Meng     /* ISSI */
21662d10766SBin Meng     { INFO("is25lq040b",  0x9d4013,      0,  64 << 10,   8, ER_4K) },
21762d10766SBin Meng     { INFO("is25lp080d",  0x9d6014,      0,  64 << 10,  16, ER_4K) },
21862d10766SBin Meng     { INFO("is25lp016d",  0x9d6015,      0,  64 << 10,  32, ER_4K) },
21962d10766SBin Meng     { INFO("is25lp032",   0x9d6016,      0,  64 << 10,  64, ER_4K) },
22062d10766SBin Meng     { INFO("is25lp064",   0x9d6017,      0,  64 << 10, 128, ER_4K) },
22162d10766SBin Meng     { INFO("is25lp128",   0x9d6018,      0,  64 << 10, 256, ER_4K) },
22262d10766SBin Meng     { INFO("is25lp256",   0x9d6019,      0,  64 << 10, 512, ER_4K) },
22362d10766SBin Meng     { INFO("is25wp032",   0x9d7016,      0,  64 << 10,  64, ER_4K) },
22462d10766SBin Meng     { INFO("is25wp064",   0x9d7017,      0,  64 << 10, 128, ER_4K) },
22562d10766SBin Meng     { INFO("is25wp128",   0x9d7018,      0,  64 << 10, 256, ER_4K) },
2263e7808deSGuenter Roeck     { INFO("is25wp256",   0x9d7019,      0,  64 << 10, 512, ER_4K),
2273e7808deSGuenter Roeck       .sfdp_read = m25p80_sfdp_is25wp256 },
22862d10766SBin Meng 
22949ab747fSPaolo Bonzini     /* Macronix */
2303e758c1dSEd Maste     { INFO("mx25l2005a",  0xc22012,      0,  64 << 10,   4, ER_4K) },
23149ab747fSPaolo Bonzini     { INFO("mx25l4005a",  0xc22013,      0,  64 << 10,   8, ER_4K) },
23249ab747fSPaolo Bonzini     { INFO("mx25l8005",   0xc22014,      0,  64 << 10,  16, 0) },
23349ab747fSPaolo Bonzini     { INFO("mx25l1606e",  0xc22015,      0,  64 << 10,  32, ER_4K) },
23449ab747fSPaolo Bonzini     { INFO("mx25l3205d",  0xc22016,      0,  64 << 10,  64, 0) },
23549ab747fSPaolo Bonzini     { INFO("mx25l6405d",  0xc22017,      0,  64 << 10, 128, 0) },
23649ab747fSPaolo Bonzini     { INFO("mx25l12805d", 0xc22018,      0,  64 << 10, 256, 0) },
23749ab747fSPaolo Bonzini     { INFO("mx25l12855e", 0xc22618,      0,  64 << 10, 256, 0) },
2380c14a3c7SCédric Le Goater     { INFO6("mx25l25635e", 0xc22019,     0xc22019,  64 << 10, 512,
239dc907a66SCédric Le Goater             ER_4K | ER_32K), .sfdp_read = m25p80_sfdp_mx25l25635e },
24051f4613dSCédric Le Goater     { INFO6("mx25l25635f", 0xc22019,     0xc22019,  64 << 10, 512,
24151f4613dSCédric Le Goater             ER_4K | ER_32K), .sfdp_read = m25p80_sfdp_mx25l25635f },
24249ab747fSPaolo Bonzini     { INFO("mx25l25655e", 0xc22619,      0,  64 << 10, 512, 0) },
243ddd8ab19SIgor Kononenko     { INFO("mx66l51235f", 0xc2201a,      0,  64 << 10, 1024, ER_4K | ER_32K) },
244dadb2f90SMarcin Krzeminski     { INFO("mx66u51235f", 0xc2253a,      0,  64 << 10, 1024, ER_4K | ER_32K) },
245dadb2f90SMarcin Krzeminski     { INFO("mx66u1g45g",  0xc2253b,      0,  64 << 10, 2048, ER_4K | ER_32K) },
24652514908SCédric Le Goater     { INFO("mx66l1g45g",  0xc2201b,      0,  64 << 10, 2048, ER_4K | ER_32K),
24752514908SCédric Le Goater       .sfdp_read = m25p80_sfdp_mx66l1g45g },
24849ab747fSPaolo Bonzini 
2493e758c1dSEd Maste     /* Micron */
250f5aac8e0SEd Maste     { INFO("n25q032a11",  0x20bb16,      0,  64 << 10,  64, ER_4K) },
251f5aac8e0SEd Maste     { INFO("n25q032a13",  0x20ba16,      0,  64 << 10,  64, ER_4K) },
252f5aac8e0SEd Maste     { INFO("n25q064a11",  0x20bb17,      0,  64 << 10, 128, ER_4K) },
253f5aac8e0SEd Maste     { INFO("n25q064a13",  0x20ba17,      0,  64 << 10, 128, ER_4K) },
254f5aac8e0SEd Maste     { INFO("n25q128a11",  0x20bb18,      0,  64 << 10, 256, ER_4K) },
255f5aac8e0SEd Maste     { INFO("n25q128a13",  0x20ba18,      0,  64 << 10, 256, ER_4K) },
256f5aac8e0SEd Maste     { INFO("n25q256a11",  0x20bb19,      0,  64 << 10, 512, ER_4K) },
2575eb24fbdSCédric Le Goater     { INFO("n25q256a13",  0x20ba19,      0,  64 << 10, 512, ER_4K),
2585eb24fbdSCédric Le Goater       .sfdp_read = m25p80_sfdp_n25q256a },
25953dc9c79SFrancisco Iglesias     { INFO("n25q512a11",  0x20bb20,      0,  64 << 10, 1024, ER_4K) },
26053dc9c79SFrancisco Iglesias     { INFO("n25q512a13",  0x20ba20,      0,  64 << 10, 1024, ER_4K) },
261dadb2f90SMarcin Krzeminski     { INFO("n25q128",     0x20ba18,      0,  64 << 10, 256, 0) },
2622113a128SIris Chen     { INFO("n25q256a",    0x20ba19,      0,  64 << 10, 512,
2635eb24fbdSCédric Le Goater            ER_4K | HAS_SR_BP3_BIT6 | HAS_SR_TB),
2645eb24fbdSCédric Le Goater       .sfdp_read = m25p80_sfdp_n25q256a },
265dadb2f90SMarcin Krzeminski    { INFO("n25q512a",    0x20ba20,      0,  64 << 10, 1024, ER_4K) },
26631fc566fSCédric Le Goater     { INFO("n25q512ax3",  0x20ba20,  0x1000,  64 << 10, 1024, ER_4K) },
267ddd8ab19SIgor Kononenko     { INFO("mt25ql512ab", 0x20ba20, 0x1044, 64 << 10, 1024, ER_4K | ER_32K) },
2686b3fac72SFrancisco Iglesias     { INFO_STACKED("mt35xu01g", 0x2c5b1b, 0x104100, 128 << 10, 1024,
2696b3fac72SFrancisco Iglesias                    ER_4K | ER_32K, 2) },
270eca27213SMarcin Krzeminski     { INFO_STACKED("n25q00",    0x20ba21, 0x1000, 64 << 10, 2048, ER_4K, 4) },
271eca27213SMarcin Krzeminski     { INFO_STACKED("n25q00a",   0x20bb21, 0x1000, 64 << 10, 2048, ER_4K, 4) },
272eca27213SMarcin Krzeminski     { INFO_STACKED("mt25ql01g", 0x20ba21, 0x1040, 64 << 10, 2048, ER_4K, 2) },
273eca27213SMarcin Krzeminski     { INFO_STACKED("mt25qu01g", 0x20bb21, 0x1040, 64 << 10, 2048, ER_4K, 2) },
274d24aa324SCédric Le Goater     { INFO_STACKED("mt25ql02g", 0x20ba22, 0x1040, 64 << 10, 4096, ER_4K | ER_32K, 2) },
275d24aa324SCédric Le Goater     { INFO_STACKED("mt25qu02g", 0x20bb22, 0x1040, 64 << 10, 4096, ER_4K | ER_32K, 2) },
2763e758c1dSEd Maste 
27749ab747fSPaolo Bonzini     /* Spansion -- single (large) sector size only, at least
27849ab747fSPaolo Bonzini      * for the chips listed here (without boot sectors).
27949ab747fSPaolo Bonzini      */
28049ab747fSPaolo Bonzini     { INFO("s25sl032p",   0x010215, 0x4d00,  64 << 10,  64, ER_4K) },
2813e758c1dSEd Maste     { INFO("s25sl064p",   0x010216, 0x4d00,  64 << 10, 128, ER_4K) },
28249ab747fSPaolo Bonzini     { INFO("s25fl256s0",  0x010219, 0x4d00, 256 << 10, 128, 0) },
28349ab747fSPaolo Bonzini     { INFO("s25fl256s1",  0x010219, 0x4d01,  64 << 10, 512, 0) },
284dadb2f90SMarcin Krzeminski     { INFO6("s25fl512s",  0x010220, 0x4d0080, 256 << 10, 256, 0) },
285dadb2f90SMarcin Krzeminski     { INFO6("s70fl01gs",  0x010221, 0x4d0080, 256 << 10, 512, 0) },
28649ab747fSPaolo Bonzini     { INFO("s25sl12800",  0x012018, 0x0300, 256 << 10,  64, 0) },
28749ab747fSPaolo Bonzini     { INFO("s25sl12801",  0x012018, 0x0301,  64 << 10, 256, 0) },
28849ab747fSPaolo Bonzini     { INFO("s25fl129p0",  0x012018, 0x4d00, 256 << 10,  64, 0) },
28949ab747fSPaolo Bonzini     { INFO("s25fl129p1",  0x012018, 0x4d01,  64 << 10, 256, 0) },
2903e758c1dSEd Maste     { INFO("s25sl004a",   0x010212,      0,  64 << 10,   8, 0) },
2913e758c1dSEd Maste     { INFO("s25sl008a",   0x010213,      0,  64 << 10,  16, 0) },
2923e758c1dSEd Maste     { INFO("s25sl016a",   0x010214,      0,  64 << 10,  32, 0) },
2933e758c1dSEd Maste     { INFO("s25sl032a",   0x010215,      0,  64 << 10,  64, 0) },
2943e758c1dSEd Maste     { INFO("s25sl064a",   0x010216,      0,  64 << 10, 128, 0) },
29549ab747fSPaolo Bonzini     { INFO("s25fl016k",   0xef4015,      0,  64 << 10,  32, ER_4K | ER_32K) },
29649ab747fSPaolo Bonzini     { INFO("s25fl064k",   0xef4017,      0,  64 << 10, 128, ER_4K | ER_32K) },
29749ab747fSPaolo Bonzini 
298dadb2f90SMarcin Krzeminski     /* Spansion --  boot sectors support  */
299dadb2f90SMarcin Krzeminski     { INFO6("s25fs512s",    0x010220, 0x4d0081, 256 << 10, 256, 0) },
300dadb2f90SMarcin Krzeminski     { INFO6("s70fs01gs",    0x010221, 0x4d0081, 256 << 10, 512, 0) },
301dadb2f90SMarcin Krzeminski 
30249ab747fSPaolo Bonzini     /* SST -- large erase sizes are "overlays", "sectors" are 4<< 10 */
30349ab747fSPaolo Bonzini     { INFO("sst25vf040b", 0xbf258d,      0,  64 << 10,   8, ER_4K) },
30449ab747fSPaolo Bonzini     { INFO("sst25vf080b", 0xbf258e,      0,  64 << 10,  16, ER_4K) },
30549ab747fSPaolo Bonzini     { INFO("sst25vf016b", 0xbf2541,      0,  64 << 10,  32, ER_4K) },
30649ab747fSPaolo Bonzini     { INFO("sst25vf032b", 0xbf254a,      0,  64 << 10,  64, ER_4K) },
30749ab747fSPaolo Bonzini     { INFO("sst25wf512",  0xbf2501,      0,  64 << 10,   1, ER_4K) },
30849ab747fSPaolo Bonzini     { INFO("sst25wf010",  0xbf2502,      0,  64 << 10,   2, ER_4K) },
30949ab747fSPaolo Bonzini     { INFO("sst25wf020",  0xbf2503,      0,  64 << 10,   4, ER_4K) },
31049ab747fSPaolo Bonzini     { INFO("sst25wf040",  0xbf2504,      0,  64 << 10,   8, ER_4K) },
311d857c4c0SAlistair Francis     { INFO("sst25wf080",  0xbf2505,      0,  64 << 10,  16, ER_4K) },
31249ab747fSPaolo Bonzini 
31349ab747fSPaolo Bonzini     /* ST Microelectronics -- newer production may have feature updates */
31449ab747fSPaolo Bonzini     { INFO("m25p05",      0x202010,      0,  32 << 10,   2, 0) },
31549ab747fSPaolo Bonzini     { INFO("m25p10",      0x202011,      0,  32 << 10,   4, 0) },
31649ab747fSPaolo Bonzini     { INFO("m25p20",      0x202012,      0,  64 << 10,   4, 0) },
31749ab747fSPaolo Bonzini     { INFO("m25p40",      0x202013,      0,  64 << 10,   8, 0) },
31849ab747fSPaolo Bonzini     { INFO("m25p80",      0x202014,      0,  64 << 10,  16, 0) },
31949ab747fSPaolo Bonzini     { INFO("m25p16",      0x202015,      0,  64 << 10,  32, 0) },
32049ab747fSPaolo Bonzini     { INFO("m25p32",      0x202016,      0,  64 << 10,  64, 0) },
32149ab747fSPaolo Bonzini     { INFO("m25p64",      0x202017,      0,  64 << 10, 128, 0) },
32249ab747fSPaolo Bonzini     { INFO("m25p128",     0x202018,      0, 256 << 10,  64, 0) },
3233e758c1dSEd Maste     { INFO("n25q032",     0x20ba16,      0,  64 << 10,  64, 0) },
32449ab747fSPaolo Bonzini 
32549ab747fSPaolo Bonzini     { INFO("m45pe10",     0x204011,      0,  64 << 10,   2, 0) },
32649ab747fSPaolo Bonzini     { INFO("m45pe80",     0x204014,      0,  64 << 10,  16, 0) },
32749ab747fSPaolo Bonzini     { INFO("m45pe16",     0x204015,      0,  64 << 10,  32, 0) },
32849ab747fSPaolo Bonzini 
3293e758c1dSEd Maste     { INFO("m25pe20",     0x208012,      0,  64 << 10,   4, 0) },
33049ab747fSPaolo Bonzini     { INFO("m25pe80",     0x208014,      0,  64 << 10,  16, 0) },
33149ab747fSPaolo Bonzini     { INFO("m25pe16",     0x208015,      0,  64 << 10,  32, ER_4K) },
33249ab747fSPaolo Bonzini 
33349ab747fSPaolo Bonzini     { INFO("m25px32",     0x207116,      0,  64 << 10,  64, ER_4K) },
33449ab747fSPaolo Bonzini     { INFO("m25px32-s0",  0x207316,      0,  64 << 10,  64, ER_4K) },
33549ab747fSPaolo Bonzini     { INFO("m25px32-s1",  0x206316,      0,  64 << 10,  64, ER_4K) },
33649ab747fSPaolo Bonzini     { INFO("m25px64",     0x207117,      0,  64 << 10, 128, 0) },
33749ab747fSPaolo Bonzini 
33849ab747fSPaolo Bonzini     /* Winbond -- w25x "blocks" are 64k, "sectors" are 4KiB */
33949ab747fSPaolo Bonzini     { INFO("w25x10",      0xef3011,      0,  64 << 10,   2, ER_4K) },
34049ab747fSPaolo Bonzini     { INFO("w25x20",      0xef3012,      0,  64 << 10,   4, ER_4K) },
34149ab747fSPaolo Bonzini     { INFO("w25x40",      0xef3013,      0,  64 << 10,   8, ER_4K) },
34249ab747fSPaolo Bonzini     { INFO("w25x80",      0xef3014,      0,  64 << 10,  16, ER_4K) },
34349ab747fSPaolo Bonzini     { INFO("w25x16",      0xef3015,      0,  64 << 10,  32, ER_4K) },
34449ab747fSPaolo Bonzini     { INFO("w25x32",      0xef3016,      0,  64 << 10,  64, ER_4K) },
34549ab747fSPaolo Bonzini     { INFO("w25q32",      0xef4016,      0,  64 << 10,  64, ER_4K) },
3463e758c1dSEd Maste     { INFO("w25q32dw",    0xef6016,      0,  64 << 10,  64, ER_4K) },
34749ab747fSPaolo Bonzini     { INFO("w25x64",      0xef3017,      0,  64 << 10, 128, ER_4K) },
34849ab747fSPaolo Bonzini     { INFO("w25q64",      0xef4017,      0,  64 << 10, 128, ER_4K) },
3493e758c1dSEd Maste     { INFO("w25q80",      0xef5014,      0,  64 << 10,  16, ER_4K) },
3503e758c1dSEd Maste     { INFO("w25q80bl",    0xef4014,      0,  64 << 10,  16, ER_4K) },
351e9041884SCédric Le Goater     { INFO("w25q256",     0xef4019,      0,  64 << 10, 512, ER_4K),
352e9041884SCédric Le Goater       .sfdp_read = m25p80_sfdp_w25q256 },
3538e57da58SCédric Le Goater     { INFO("w25q512jv",   0xef4020,      0,  64 << 10, 1024, ER_4K),
3548e57da58SCédric Le Goater       .sfdp_read = m25p80_sfdp_w25q512jv },
355a34b0d53SPatrick Williams     { INFO("w25q01jvq",   0xef4021,      0,  64 << 10, 2048, ER_4K),
356a34b0d53SPatrick Williams       .sfdp_read = m25p80_sfdp_w25q01jvq },
35749ab747fSPaolo Bonzini };
35849ab747fSPaolo Bonzini 
35949ab747fSPaolo Bonzini typedef enum {
36049ab747fSPaolo Bonzini     NOP = 0,
36149ab747fSPaolo Bonzini     WRSR = 0x1,
36249ab747fSPaolo Bonzini     WRDI = 0x4,
36349ab747fSPaolo Bonzini     RDSR = 0x5,
36449ab747fSPaolo Bonzini     WREN = 0x6,
3650f589782SFrancisco Iglesias     BRRD = 0x16,
3660f589782SFrancisco Iglesias     BRWR = 0x17,
36749ab747fSPaolo Bonzini     JEDEC_READ = 0x9f,
3680f589782SFrancisco Iglesias     BULK_ERASE_60 = 0x60,
36949ab747fSPaolo Bonzini     BULK_ERASE = 0xc7,
3709fbaa364SMarcin Krzeminski     READ_FSR = 0x70,
3717a69c100SMarcin Krzeminski     RDCR = 0x15,
3722389bcc2SCédric Le Goater     RDSFDP = 0x5a,
37349ab747fSPaolo Bonzini 
37463e47f6fSMarcin Krzeminski     READ = 0x03,
37563e47f6fSMarcin Krzeminski     READ4 = 0x13,
37663e47f6fSMarcin Krzeminski     FAST_READ = 0x0b,
37763e47f6fSMarcin Krzeminski     FAST_READ4 = 0x0c,
37849ab747fSPaolo Bonzini     DOR = 0x3b,
37963e47f6fSMarcin Krzeminski     DOR4 = 0x3c,
38049ab747fSPaolo Bonzini     QOR = 0x6b,
38163e47f6fSMarcin Krzeminski     QOR4 = 0x6c,
38249ab747fSPaolo Bonzini     DIOR = 0xbb,
38363e47f6fSMarcin Krzeminski     DIOR4 = 0xbc,
38449ab747fSPaolo Bonzini     QIOR = 0xeb,
38563e47f6fSMarcin Krzeminski     QIOR4 = 0xec,
38649ab747fSPaolo Bonzini 
38763e47f6fSMarcin Krzeminski     PP = 0x02,
38863e47f6fSMarcin Krzeminski     PP4 = 0x12,
38930467afeSMarcin Krzeminski     PP4_4 = 0x3e,
39049ab747fSPaolo Bonzini     DPP = 0xa2,
39149ab747fSPaolo Bonzini     QPP = 0x32,
392597c15f0SMarcin Krzeminski     QPP_4 = 0x34,
393a87fc364SFrancisco Iglesias     RDID_90 = 0x90,
394a87fc364SFrancisco Iglesias     RDID_AB = 0xab,
395465ef47aSXuzhou Cheng     AAI_WP = 0xad,
39649ab747fSPaolo Bonzini 
39749ab747fSPaolo Bonzini     ERASE_4K = 0x20,
39863e47f6fSMarcin Krzeminski     ERASE4_4K = 0x21,
39949ab747fSPaolo Bonzini     ERASE_32K = 0x52,
40030467afeSMarcin Krzeminski     ERASE4_32K = 0x5c,
40149ab747fSPaolo Bonzini     ERASE_SECTOR = 0xd8,
40263e47f6fSMarcin Krzeminski     ERASE4_SECTOR = 0xdc,
403187c2636SMarcin Krzeminski 
404c0f3f675SMarcin Krzeminski     EN_4BYTE_ADDR = 0xB7,
405c0f3f675SMarcin Krzeminski     EX_4BYTE_ADDR = 0xE9,
406c0f3f675SMarcin Krzeminski 
407d8a29a7aSMarcin Krzeminski     EXTEND_ADDR_READ = 0xC8,
408d8a29a7aSMarcin Krzeminski     EXTEND_ADDR_WRITE = 0xC5,
409d8a29a7aSMarcin Krzeminski 
410187c2636SMarcin Krzeminski     RESET_ENABLE = 0x66,
411187c2636SMarcin Krzeminski     RESET_MEMORY = 0x99,
412cb475951SMarcin Krzeminski 
4137a69c100SMarcin Krzeminski     /*
4147a69c100SMarcin Krzeminski      * Micron: 0x35 - enable QPI
4157a69c100SMarcin Krzeminski      * Spansion: 0x35 - read control register
4167a69c100SMarcin Krzeminski      */
4177a69c100SMarcin Krzeminski     RDCR_EQIO = 0x35,
4187a69c100SMarcin Krzeminski     RSTQIO = 0xf5,
4197a69c100SMarcin Krzeminski 
420cb475951SMarcin Krzeminski     RNVCR = 0xB5,
421cb475951SMarcin Krzeminski     WNVCR = 0xB1,
422cb475951SMarcin Krzeminski 
423cb475951SMarcin Krzeminski     RVCR = 0x85,
424cb475951SMarcin Krzeminski     WVCR = 0x81,
425cb475951SMarcin Krzeminski 
426cb475951SMarcin Krzeminski     REVCR = 0x65,
427cb475951SMarcin Krzeminski     WEVCR = 0x61,
428f509dfeeSMarcin Krzeminski 
429f509dfeeSMarcin Krzeminski     DIE_ERASE = 0xC4,
43049ab747fSPaolo Bonzini } FlashCMD;
43149ab747fSPaolo Bonzini 
43249ab747fSPaolo Bonzini typedef enum {
43349ab747fSPaolo Bonzini     STATE_IDLE,
43449ab747fSPaolo Bonzini     STATE_PAGE_PROGRAM,
43549ab747fSPaolo Bonzini     STATE_READ,
43649ab747fSPaolo Bonzini     STATE_COLLECTING_DATA,
4379964674eSMarcin Krzeminski     STATE_COLLECTING_VAR_LEN_DATA,
43849ab747fSPaolo Bonzini     STATE_READING_DATA,
4392389bcc2SCédric Le Goater     STATE_READING_SFDP,
44049ab747fSPaolo Bonzini } CMDState;
44149ab747fSPaolo Bonzini 
442c7cd0a6cSMarcin Krzeminski typedef enum {
443c7cd0a6cSMarcin Krzeminski     MAN_SPANSION,
444c7cd0a6cSMarcin Krzeminski     MAN_MACRONIX,
445c7cd0a6cSMarcin Krzeminski     MAN_NUMONYX,
446c7cd0a6cSMarcin Krzeminski     MAN_WINBOND,
447a87fc364SFrancisco Iglesias     MAN_SST,
44810509e10SBin Meng     MAN_ISSI,
449c7cd0a6cSMarcin Krzeminski     MAN_GENERIC,
450c7cd0a6cSMarcin Krzeminski } Manufacturer;
451c7cd0a6cSMarcin Krzeminski 
45223486231SJoe Komlodi typedef enum {
45323486231SJoe Komlodi     MODE_STD = 0,
45423486231SJoe Komlodi     MODE_DIO = 1,
45523486231SJoe Komlodi     MODE_QIO = 2
45623486231SJoe Komlodi } SPIMode;
45723486231SJoe Komlodi 
45824cb2e0dSJean-Christophe Dubois #define M25P80_INTERNAL_DATA_BUFFER_SZ 16
45924cb2e0dSJean-Christophe Dubois 
460db1015e9SEduardo Habkost struct Flash {
461ec7e429bSPhilippe Mathieu-Daudé     SSIPeripheral parent_obj;
462cdccf7d7SPeter Crosthwaite 
4634be74634SMarkus Armbruster     BlockBackend *blk;
46449ab747fSPaolo Bonzini 
46549ab747fSPaolo Bonzini     uint8_t *storage;
46649ab747fSPaolo Bonzini     uint32_t size;
46749ab747fSPaolo Bonzini     int page_size;
46849ab747fSPaolo Bonzini 
46949ab747fSPaolo Bonzini     uint8_t state;
47024cb2e0dSJean-Christophe Dubois     uint8_t data[M25P80_INTERNAL_DATA_BUFFER_SZ];
47149ab747fSPaolo Bonzini     uint32_t len;
47249ab747fSPaolo Bonzini     uint32_t pos;
4730add925fSFrancisco Iglesias     bool data_read_loop;
47449ab747fSPaolo Bonzini     uint8_t needed_bytes;
47549ab747fSPaolo Bonzini     uint8_t cmd_in_progress;
476b7f480c3SPaolo Bonzini     uint32_t cur_addr;
477cb475951SMarcin Krzeminski     uint32_t nonvolatile_cfg;
478d9cc8701SMarcin Krzeminski     /* Configuration register for Macronix */
479cb475951SMarcin Krzeminski     uint32_t volatile_cfg;
480cb475951SMarcin Krzeminski     uint32_t enh_volatile_cfg;
481d9cc8701SMarcin Krzeminski     /* Spansion cfg registers. */
482d9cc8701SMarcin Krzeminski     uint8_t spansion_cr1nv;
483d9cc8701SMarcin Krzeminski     uint8_t spansion_cr2nv;
484d9cc8701SMarcin Krzeminski     uint8_t spansion_cr3nv;
485d9cc8701SMarcin Krzeminski     uint8_t spansion_cr4nv;
486d9cc8701SMarcin Krzeminski     uint8_t spansion_cr1v;
487d9cc8701SMarcin Krzeminski     uint8_t spansion_cr2v;
488d9cc8701SMarcin Krzeminski     uint8_t spansion_cr3v;
489d9cc8701SMarcin Krzeminski     uint8_t spansion_cr4v;
4902fa22a0fSIris Chen     bool wp_level;
49149ab747fSPaolo Bonzini     bool write_enable;
492c0f3f675SMarcin Krzeminski     bool four_bytes_address_mode;
493187c2636SMarcin Krzeminski     bool reset_enable;
4947a69c100SMarcin Krzeminski     bool quad_enable;
495465ef47aSXuzhou Cheng     bool aai_enable;
4962113a128SIris Chen     bool block_protect0;
4972113a128SIris Chen     bool block_protect1;
4982113a128SIris Chen     bool block_protect2;
4992113a128SIris Chen     bool block_protect3;
5002113a128SIris Chen     bool top_bottom_bit;
5012fa22a0fSIris Chen     bool status_register_write_disabled;
502d8a29a7aSMarcin Krzeminski     uint8_t ear;
50349ab747fSPaolo Bonzini 
50449ab747fSPaolo Bonzini     int64_t dirty_page;
50549ab747fSPaolo Bonzini 
50649ab747fSPaolo Bonzini     const FlashPartInfo *pi;
50749ab747fSPaolo Bonzini 
508db1015e9SEduardo Habkost };
50949ab747fSPaolo Bonzini 
510db1015e9SEduardo Habkost struct M25P80Class {
511ec7e429bSPhilippe Mathieu-Daudé     SSIPeripheralClass parent_class;
51249ab747fSPaolo Bonzini     FlashPartInfo *pi;
513db1015e9SEduardo Habkost };
51449ab747fSPaolo Bonzini 
51549ab747fSPaolo Bonzini #define TYPE_M25P80 "m25p80-generic"
OBJECT_DECLARE_TYPE(Flash,M25P80Class,M25P80)516a489d195SEduardo Habkost OBJECT_DECLARE_TYPE(Flash, M25P80Class, M25P80)
51749ab747fSPaolo Bonzini 
518c7cd0a6cSMarcin Krzeminski static inline Manufacturer get_man(Flash *s)
519c7cd0a6cSMarcin Krzeminski {
520e3ba6cd6SMarcin Krzeminski     switch (s->pi->id[0]) {
521c7cd0a6cSMarcin Krzeminski     case 0x20:
522c7cd0a6cSMarcin Krzeminski         return MAN_NUMONYX;
523c7cd0a6cSMarcin Krzeminski     case 0xEF:
524c7cd0a6cSMarcin Krzeminski         return MAN_WINBOND;
525c7cd0a6cSMarcin Krzeminski     case 0x01:
526c7cd0a6cSMarcin Krzeminski         return MAN_SPANSION;
527c7cd0a6cSMarcin Krzeminski     case 0xC2:
528c7cd0a6cSMarcin Krzeminski         return MAN_MACRONIX;
529a87fc364SFrancisco Iglesias     case 0xBF:
530a87fc364SFrancisco Iglesias         return MAN_SST;
53110509e10SBin Meng     case 0x9D:
53210509e10SBin Meng         return MAN_ISSI;
533c7cd0a6cSMarcin Krzeminski     default:
534c7cd0a6cSMarcin Krzeminski         return MAN_GENERIC;
535c7cd0a6cSMarcin Krzeminski     }
536c7cd0a6cSMarcin Krzeminski }
537c7cd0a6cSMarcin Krzeminski 
blk_sync_complete(void * opaque,int ret)5384be74634SMarkus Armbruster static void blk_sync_complete(void *opaque, int ret)
53949ab747fSPaolo Bonzini {
540cace7b80SPaolo Bonzini     QEMUIOVector *iov = opaque;
541cace7b80SPaolo Bonzini 
542cace7b80SPaolo Bonzini     qemu_iovec_destroy(iov);
543cace7b80SPaolo Bonzini     g_free(iov);
544cace7b80SPaolo Bonzini 
54549ab747fSPaolo Bonzini     /* do nothing. Masters do not directly interact with the backing store,
54649ab747fSPaolo Bonzini      * only the working copy so no mutexing required.
54749ab747fSPaolo Bonzini      */
54849ab747fSPaolo Bonzini }
54949ab747fSPaolo Bonzini 
flash_sync_page(Flash * s,int page)55049ab747fSPaolo Bonzini static void flash_sync_page(Flash *s, int page)
55149ab747fSPaolo Bonzini {
552eef9f19eSShannon Zhao     QEMUIOVector *iov;
55349ab747fSPaolo Bonzini 
55486b1cf32SKevin Wolf     if (!s->blk || !blk_is_writable(s->blk)) {
555fc1084aaSPeter Crosthwaite         return;
556fc1084aaSPeter Crosthwaite     }
557fc1084aaSPeter Crosthwaite 
558eef9f19eSShannon Zhao     iov = g_new(QEMUIOVector, 1);
559cace7b80SPaolo Bonzini     qemu_iovec_init(iov, 1);
560cace7b80SPaolo Bonzini     qemu_iovec_add(iov, s->storage + page * s->pi->page_size,
561243e6f69SEric Blake                    s->pi->page_size);
562cace7b80SPaolo Bonzini     blk_aio_pwritev(s->blk, page * s->pi->page_size, iov, 0,
563cace7b80SPaolo Bonzini                     blk_sync_complete, iov);
56449ab747fSPaolo Bonzini }
56549ab747fSPaolo Bonzini 
flash_sync_area(Flash * s,int64_t off,int64_t len)56649ab747fSPaolo Bonzini static inline void flash_sync_area(Flash *s, int64_t off, int64_t len)
56749ab747fSPaolo Bonzini {
568eef9f19eSShannon Zhao     QEMUIOVector *iov;
56949ab747fSPaolo Bonzini 
57086b1cf32SKevin Wolf     if (!s->blk || !blk_is_writable(s->blk)) {
57149ab747fSPaolo Bonzini         return;
57249ab747fSPaolo Bonzini     }
57349ab747fSPaolo Bonzini 
57449ab747fSPaolo Bonzini     assert(!(len % BDRV_SECTOR_SIZE));
575eef9f19eSShannon Zhao     iov = g_new(QEMUIOVector, 1);
576cace7b80SPaolo Bonzini     qemu_iovec_init(iov, 1);
577cace7b80SPaolo Bonzini     qemu_iovec_add(iov, s->storage + off, len);
578cace7b80SPaolo Bonzini     blk_aio_pwritev(s->blk, off, iov, 0, blk_sync_complete, iov);
57949ab747fSPaolo Bonzini }
58049ab747fSPaolo Bonzini 
flash_erase(Flash * s,int offset,FlashCMD cmd)58149ab747fSPaolo Bonzini static void flash_erase(Flash *s, int offset, FlashCMD cmd)
58249ab747fSPaolo Bonzini {
58349ab747fSPaolo Bonzini     uint32_t len;
58449ab747fSPaolo Bonzini     uint8_t capa_to_assert = 0;
58549ab747fSPaolo Bonzini 
58649ab747fSPaolo Bonzini     switch (cmd) {
58749ab747fSPaolo Bonzini     case ERASE_4K:
58863e47f6fSMarcin Krzeminski     case ERASE4_4K:
589e8400cf3SPhilippe Mathieu-Daudé         len = 4 * KiB;
59049ab747fSPaolo Bonzini         capa_to_assert = ER_4K;
59149ab747fSPaolo Bonzini         break;
59249ab747fSPaolo Bonzini     case ERASE_32K:
59330467afeSMarcin Krzeminski     case ERASE4_32K:
594e8400cf3SPhilippe Mathieu-Daudé         len = 32 * KiB;
59549ab747fSPaolo Bonzini         capa_to_assert = ER_32K;
59649ab747fSPaolo Bonzini         break;
59749ab747fSPaolo Bonzini     case ERASE_SECTOR:
59863e47f6fSMarcin Krzeminski     case ERASE4_SECTOR:
59949ab747fSPaolo Bonzini         len = s->pi->sector_size;
60049ab747fSPaolo Bonzini         break;
60149ab747fSPaolo Bonzini     case BULK_ERASE:
60249ab747fSPaolo Bonzini         len = s->size;
60349ab747fSPaolo Bonzini         break;
604f509dfeeSMarcin Krzeminski     case DIE_ERASE:
605f509dfeeSMarcin Krzeminski         if (s->pi->die_cnt) {
606f509dfeeSMarcin Krzeminski             len = s->size / s->pi->die_cnt;
607f509dfeeSMarcin Krzeminski             offset = offset & (~(len - 1));
608f509dfeeSMarcin Krzeminski         } else {
609f509dfeeSMarcin Krzeminski             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: die erase is not supported"
610f509dfeeSMarcin Krzeminski                           " by device\n");
611f509dfeeSMarcin Krzeminski             return;
612f509dfeeSMarcin Krzeminski         }
613f509dfeeSMarcin Krzeminski         break;
61449ab747fSPaolo Bonzini     default:
61549ab747fSPaolo Bonzini         abort();
61649ab747fSPaolo Bonzini     }
61749ab747fSPaolo Bonzini 
618ccc46090SGuenter Roeck     trace_m25p80_flash_erase(s, offset, len);
619ccc46090SGuenter Roeck 
62049ab747fSPaolo Bonzini     if ((s->pi->flags & capa_to_assert) != capa_to_assert) {
621e9711b4dSPeter Crosthwaite         qemu_log_mask(LOG_GUEST_ERROR, "M25P80: %d erase size not supported by"
622e9711b4dSPeter Crosthwaite                       " device\n", len);
62349ab747fSPaolo Bonzini     }
62449ab747fSPaolo Bonzini 
62549ab747fSPaolo Bonzini     if (!s->write_enable) {
626e9711b4dSPeter Crosthwaite         qemu_log_mask(LOG_GUEST_ERROR, "M25P80: erase with write protect!\n");
62749ab747fSPaolo Bonzini         return;
62849ab747fSPaolo Bonzini     }
62949ab747fSPaolo Bonzini     memset(s->storage + offset, 0xff, len);
63049ab747fSPaolo Bonzini     flash_sync_area(s, offset, len);
63149ab747fSPaolo Bonzini }
63249ab747fSPaolo Bonzini 
flash_sync_dirty(Flash * s,int64_t newpage)63349ab747fSPaolo Bonzini static inline void flash_sync_dirty(Flash *s, int64_t newpage)
63449ab747fSPaolo Bonzini {
63549ab747fSPaolo Bonzini     if (s->dirty_page >= 0 && s->dirty_page != newpage) {
63649ab747fSPaolo Bonzini         flash_sync_page(s, s->dirty_page);
63749ab747fSPaolo Bonzini         s->dirty_page = newpage;
63849ab747fSPaolo Bonzini     }
63949ab747fSPaolo Bonzini }
64049ab747fSPaolo Bonzini 
64149ab747fSPaolo Bonzini static inline
flash_write8(Flash * s,uint32_t addr,uint8_t data)642b7f480c3SPaolo Bonzini void flash_write8(Flash *s, uint32_t addr, uint8_t data)
64349ab747fSPaolo Bonzini {
644b7f480c3SPaolo Bonzini     uint32_t page = addr / s->pi->page_size;
64549ab747fSPaolo Bonzini     uint8_t prev = s->storage[s->cur_addr];
6462113a128SIris Chen     uint32_t block_protect_value = (s->block_protect3 << 3) |
6472113a128SIris Chen                                    (s->block_protect2 << 2) |
6482113a128SIris Chen                                    (s->block_protect1 << 1) |
6492113a128SIris Chen                                    (s->block_protect0 << 0);
65049ab747fSPaolo Bonzini 
65149ab747fSPaolo Bonzini     if (!s->write_enable) {
652e9711b4dSPeter Crosthwaite         qemu_log_mask(LOG_GUEST_ERROR, "M25P80: write with write protect!\n");
6531695854bSBin Meng         return;
65449ab747fSPaolo Bonzini     }
65549ab747fSPaolo Bonzini 
6562113a128SIris Chen     if (block_protect_value > 0) {
6572113a128SIris Chen         uint32_t num_protected_sectors = 1 << (block_protect_value - 1);
6582113a128SIris Chen         uint32_t sector = addr / s->pi->sector_size;
6592113a128SIris Chen 
6602113a128SIris Chen         /* top_bottom_bit == 0 means TOP */
6612113a128SIris Chen         if (!s->top_bottom_bit) {
6622113a128SIris Chen             if (s->pi->n_sectors <= sector + num_protected_sectors) {
6632113a128SIris Chen                 qemu_log_mask(LOG_GUEST_ERROR,
6642113a128SIris Chen                               "M25P80: write with write protect!\n");
6652113a128SIris Chen                 return;
6662113a128SIris Chen             }
6672113a128SIris Chen         } else {
6682113a128SIris Chen             if (sector < num_protected_sectors) {
6692113a128SIris Chen                 qemu_log_mask(LOG_GUEST_ERROR,
6702113a128SIris Chen                               "M25P80: write with write protect!\n");
6712113a128SIris Chen                 return;
6722113a128SIris Chen             }
6732113a128SIris Chen         }
6742113a128SIris Chen     }
6752113a128SIris Chen 
67649ab747fSPaolo Bonzini     if ((prev ^ data) & data) {
677ccc46090SGuenter Roeck         trace_m25p80_programming_zero_to_one(s, addr, prev, data);
67849ab747fSPaolo Bonzini     }
67949ab747fSPaolo Bonzini 
6801435bcd6SMarcin Krzeminski     if (s->pi->flags & EEPROM) {
68149ab747fSPaolo Bonzini         s->storage[s->cur_addr] = data;
68249ab747fSPaolo Bonzini     } else {
68349ab747fSPaolo Bonzini         s->storage[s->cur_addr] &= data;
68449ab747fSPaolo Bonzini     }
68549ab747fSPaolo Bonzini 
68649ab747fSPaolo Bonzini     flash_sync_dirty(s, page);
68749ab747fSPaolo Bonzini     s->dirty_page = page;
68849ab747fSPaolo Bonzini }
68949ab747fSPaolo Bonzini 
get_addr_length(Flash * s)690c0f3f675SMarcin Krzeminski static inline int get_addr_length(Flash *s)
691c0f3f675SMarcin Krzeminski {
6921435bcd6SMarcin Krzeminski    /* check if eeprom is in use */
6931435bcd6SMarcin Krzeminski     if (s->pi->flags == EEPROM) {
6941435bcd6SMarcin Krzeminski         return 2;
6951435bcd6SMarcin Krzeminski     }
6961435bcd6SMarcin Krzeminski 
69763e47f6fSMarcin Krzeminski    switch (s->cmd_in_progress) {
6982389bcc2SCédric Le Goater    case RDSFDP:
6992389bcc2SCédric Le Goater        return 3;
70063e47f6fSMarcin Krzeminski    case PP4:
70130467afeSMarcin Krzeminski    case PP4_4:
702597c15f0SMarcin Krzeminski    case QPP_4:
70363e47f6fSMarcin Krzeminski    case READ4:
70463e47f6fSMarcin Krzeminski    case QIOR4:
70563e47f6fSMarcin Krzeminski    case ERASE4_4K:
70630467afeSMarcin Krzeminski    case ERASE4_32K:
70763e47f6fSMarcin Krzeminski    case ERASE4_SECTOR:
70863e47f6fSMarcin Krzeminski    case FAST_READ4:
70963e47f6fSMarcin Krzeminski    case DOR4:
71063e47f6fSMarcin Krzeminski    case QOR4:
71163e47f6fSMarcin Krzeminski    case DIOR4:
71263e47f6fSMarcin Krzeminski        return 4;
71363e47f6fSMarcin Krzeminski    default:
714c0f3f675SMarcin Krzeminski        return s->four_bytes_address_mode ? 4 : 3;
715c0f3f675SMarcin Krzeminski    }
71663e47f6fSMarcin Krzeminski }
717c0f3f675SMarcin Krzeminski 
complete_collecting_data(Flash * s)71849ab747fSPaolo Bonzini static void complete_collecting_data(Flash *s)
71949ab747fSPaolo Bonzini {
720b68cb060SPaolo Bonzini     int i, n;
721c0f3f675SMarcin Krzeminski 
722b68cb060SPaolo Bonzini     n = get_addr_length(s);
723b68cb060SPaolo Bonzini     s->cur_addr = (n == 3 ? s->ear : 0);
724b68cb060SPaolo Bonzini     for (i = 0; i < n; ++i) {
725c0f3f675SMarcin Krzeminski         s->cur_addr <<= 8;
726c0f3f675SMarcin Krzeminski         s->cur_addr |= s->data[i];
727c0f3f675SMarcin Krzeminski     }
728c0f3f675SMarcin Krzeminski 
729b68cb060SPaolo Bonzini     s->cur_addr &= s->size - 1;
73049ab747fSPaolo Bonzini 
73149ab747fSPaolo Bonzini     s->state = STATE_IDLE;
73249ab747fSPaolo Bonzini 
733ccc46090SGuenter Roeck     trace_m25p80_complete_collecting(s, s->cmd_in_progress, n, s->ear,
734ccc46090SGuenter Roeck                                      s->cur_addr);
735ccc46090SGuenter Roeck 
73649ab747fSPaolo Bonzini     switch (s->cmd_in_progress) {
73749ab747fSPaolo Bonzini     case DPP:
73849ab747fSPaolo Bonzini     case QPP:
739597c15f0SMarcin Krzeminski     case QPP_4:
74049ab747fSPaolo Bonzini     case PP:
74163e47f6fSMarcin Krzeminski     case PP4:
74230467afeSMarcin Krzeminski     case PP4_4:
74349ab747fSPaolo Bonzini         s->state = STATE_PAGE_PROGRAM;
74449ab747fSPaolo Bonzini         break;
745465ef47aSXuzhou Cheng     case AAI_WP:
746465ef47aSXuzhou Cheng         /* AAI programming starts from the even address */
747465ef47aSXuzhou Cheng         s->cur_addr &= ~BIT(0);
748465ef47aSXuzhou Cheng         s->state = STATE_PAGE_PROGRAM;
749465ef47aSXuzhou Cheng         break;
75049ab747fSPaolo Bonzini     case READ:
75163e47f6fSMarcin Krzeminski     case READ4:
75249ab747fSPaolo Bonzini     case FAST_READ:
75363e47f6fSMarcin Krzeminski     case FAST_READ4:
75449ab747fSPaolo Bonzini     case DOR:
75563e47f6fSMarcin Krzeminski     case DOR4:
75649ab747fSPaolo Bonzini     case QOR:
75763e47f6fSMarcin Krzeminski     case QOR4:
75849ab747fSPaolo Bonzini     case DIOR:
75963e47f6fSMarcin Krzeminski     case DIOR4:
76049ab747fSPaolo Bonzini     case QIOR:
76163e47f6fSMarcin Krzeminski     case QIOR4:
76249ab747fSPaolo Bonzini         s->state = STATE_READ;
76349ab747fSPaolo Bonzini         break;
76449ab747fSPaolo Bonzini     case ERASE_4K:
76563e47f6fSMarcin Krzeminski     case ERASE4_4K:
76649ab747fSPaolo Bonzini     case ERASE_32K:
76730467afeSMarcin Krzeminski     case ERASE4_32K:
76849ab747fSPaolo Bonzini     case ERASE_SECTOR:
76963e47f6fSMarcin Krzeminski     case ERASE4_SECTOR:
770f509dfeeSMarcin Krzeminski     case DIE_ERASE:
77149ab747fSPaolo Bonzini         flash_erase(s, s->cur_addr, s->cmd_in_progress);
77249ab747fSPaolo Bonzini         break;
77349ab747fSPaolo Bonzini     case WRSR:
7742fa22a0fSIris Chen         s->status_register_write_disabled = extract32(s->data[0], 7, 1);
7752113a128SIris Chen         s->block_protect0 = extract32(s->data[0], 2, 1);
7762113a128SIris Chen         s->block_protect1 = extract32(s->data[0], 3, 1);
7772113a128SIris Chen         s->block_protect2 = extract32(s->data[0], 4, 1);
7782113a128SIris Chen         if (s->pi->flags & HAS_SR_TB) {
7792113a128SIris Chen             s->top_bottom_bit = extract32(s->data[0], 5, 1);
7802113a128SIris Chen         }
7812113a128SIris Chen         if (s->pi->flags & HAS_SR_BP3_BIT6) {
7822113a128SIris Chen             s->block_protect3 = extract32(s->data[0], 6, 1);
7832113a128SIris Chen         }
7842fa22a0fSIris Chen 
7857a69c100SMarcin Krzeminski         switch (get_man(s)) {
7867a69c100SMarcin Krzeminski         case MAN_SPANSION:
7877a69c100SMarcin Krzeminski             s->quad_enable = !!(s->data[1] & 0x02);
7887a69c100SMarcin Krzeminski             break;
78910509e10SBin Meng         case MAN_ISSI:
79010509e10SBin Meng             s->quad_enable = extract32(s->data[0], 6, 1);
79110509e10SBin Meng             break;
7927a69c100SMarcin Krzeminski         case MAN_MACRONIX:
7937a69c100SMarcin Krzeminski             s->quad_enable = extract32(s->data[0], 6, 1);
794d9cc8701SMarcin Krzeminski             if (s->len > 1) {
7952151b044SCédric Le Goater                 s->volatile_cfg = s->data[1];
796d9cc8701SMarcin Krzeminski                 s->four_bytes_address_mode = extract32(s->data[1], 5, 1);
797d9cc8701SMarcin Krzeminski             }
7987a69c100SMarcin Krzeminski             break;
7997a69c100SMarcin Krzeminski         default:
8007a69c100SMarcin Krzeminski             break;
8017a69c100SMarcin Krzeminski         }
80249ab747fSPaolo Bonzini         if (s->write_enable) {
80349ab747fSPaolo Bonzini             s->write_enable = false;
80449ab747fSPaolo Bonzini         }
80549ab747fSPaolo Bonzini         break;
8060f589782SFrancisco Iglesias     case BRWR:
807d8a29a7aSMarcin Krzeminski     case EXTEND_ADDR_WRITE:
808d8a29a7aSMarcin Krzeminski         s->ear = s->data[0];
809d8a29a7aSMarcin Krzeminski         break;
810cb475951SMarcin Krzeminski     case WNVCR:
811cb475951SMarcin Krzeminski         s->nonvolatile_cfg = s->data[0] | (s->data[1] << 8);
812cb475951SMarcin Krzeminski         break;
813cb475951SMarcin Krzeminski     case WVCR:
814cb475951SMarcin Krzeminski         s->volatile_cfg = s->data[0];
815cb475951SMarcin Krzeminski         break;
816cb475951SMarcin Krzeminski     case WEVCR:
817cb475951SMarcin Krzeminski         s->enh_volatile_cfg = s->data[0];
818cb475951SMarcin Krzeminski         break;
819a87fc364SFrancisco Iglesias     case RDID_90:
820a87fc364SFrancisco Iglesias     case RDID_AB:
821a87fc364SFrancisco Iglesias         if (get_man(s) == MAN_SST) {
822a87fc364SFrancisco Iglesias             if (s->cur_addr <= 1) {
823a87fc364SFrancisco Iglesias                 if (s->cur_addr) {
824a87fc364SFrancisco Iglesias                     s->data[0] = s->pi->id[2];
825a87fc364SFrancisco Iglesias                     s->data[1] = s->pi->id[0];
826a87fc364SFrancisco Iglesias                 } else {
827a87fc364SFrancisco Iglesias                     s->data[0] = s->pi->id[0];
828a87fc364SFrancisco Iglesias                     s->data[1] = s->pi->id[2];
829a87fc364SFrancisco Iglesias                 }
830a87fc364SFrancisco Iglesias                 s->pos = 0;
831a87fc364SFrancisco Iglesias                 s->len = 2;
832a87fc364SFrancisco Iglesias                 s->data_read_loop = true;
833a87fc364SFrancisco Iglesias                 s->state = STATE_READING_DATA;
834a87fc364SFrancisco Iglesias             } else {
835a87fc364SFrancisco Iglesias                 qemu_log_mask(LOG_GUEST_ERROR,
836a87fc364SFrancisco Iglesias                               "M25P80: Invalid read id address\n");
837a87fc364SFrancisco Iglesias             }
838a87fc364SFrancisco Iglesias         } else {
839a87fc364SFrancisco Iglesias             qemu_log_mask(LOG_GUEST_ERROR,
840a87fc364SFrancisco Iglesias                           "M25P80: Read id (command 0x90/0xAB) is not supported"
841a87fc364SFrancisco Iglesias                           " by device\n");
842a87fc364SFrancisco Iglesias         }
843a87fc364SFrancisco Iglesias         break;
8442389bcc2SCédric Le Goater 
8452389bcc2SCédric Le Goater     case RDSFDP:
8462389bcc2SCédric Le Goater         s->state = STATE_READING_SFDP;
8472389bcc2SCédric Le Goater         break;
8482389bcc2SCédric Le Goater 
84949ab747fSPaolo Bonzini     default:
85049ab747fSPaolo Bonzini         break;
85149ab747fSPaolo Bonzini     }
85249ab747fSPaolo Bonzini }
85349ab747fSPaolo Bonzini 
reset_memory(Flash * s)854187c2636SMarcin Krzeminski static void reset_memory(Flash *s)
855187c2636SMarcin Krzeminski {
856187c2636SMarcin Krzeminski     s->cmd_in_progress = NOP;
857187c2636SMarcin Krzeminski     s->cur_addr = 0;
858d8a29a7aSMarcin Krzeminski     s->ear = 0;
859c0f3f675SMarcin Krzeminski     s->four_bytes_address_mode = false;
860187c2636SMarcin Krzeminski     s->len = 0;
861187c2636SMarcin Krzeminski     s->needed_bytes = 0;
862187c2636SMarcin Krzeminski     s->pos = 0;
863187c2636SMarcin Krzeminski     s->state = STATE_IDLE;
864187c2636SMarcin Krzeminski     s->write_enable = false;
865187c2636SMarcin Krzeminski     s->reset_enable = false;
8667a69c100SMarcin Krzeminski     s->quad_enable = false;
867465ef47aSXuzhou Cheng     s->aai_enable = false;
868187c2636SMarcin Krzeminski 
869c7cd0a6cSMarcin Krzeminski     switch (get_man(s)) {
870c7cd0a6cSMarcin Krzeminski     case MAN_NUMONYX:
871cb475951SMarcin Krzeminski         s->volatile_cfg = 0;
872cb475951SMarcin Krzeminski         s->volatile_cfg |= VCFG_DUMMY;
873cb475951SMarcin Krzeminski         s->volatile_cfg |= VCFG_WRAP_SEQUENTIAL;
874cb475951SMarcin Krzeminski         if ((s->nonvolatile_cfg & NVCFG_XIP_MODE_MASK)
875fc5df349SJoe Komlodi                                 == NVCFG_XIP_MODE_DISABLED) {
87609414144SJoe Komlodi             s->volatile_cfg |= VCFG_XIP_MODE_DISABLED;
877cb475951SMarcin Krzeminski         }
878cb475951SMarcin Krzeminski         s->volatile_cfg |= deposit32(s->volatile_cfg,
879cb475951SMarcin Krzeminski                             VCFG_DUMMY_CLK_POS,
880cb475951SMarcin Krzeminski                             CFG_DUMMY_CLK_LEN,
881cb475951SMarcin Krzeminski                             extract32(s->nonvolatile_cfg,
882cb475951SMarcin Krzeminski                                         NVCFG_DUMMY_CLK_POS,
883cb475951SMarcin Krzeminski                                         CFG_DUMMY_CLK_LEN)
884cb475951SMarcin Krzeminski                             );
885cb475951SMarcin Krzeminski 
886cb475951SMarcin Krzeminski         s->enh_volatile_cfg = 0;
8875c765e7aSStefan Weil         s->enh_volatile_cfg |= EVCFG_OUT_DRIVER_STRENGTH_DEF;
888cb475951SMarcin Krzeminski         s->enh_volatile_cfg |= EVCFG_VPP_ACCELERATOR;
889cb475951SMarcin Krzeminski         s->enh_volatile_cfg |= EVCFG_RESET_HOLD_ENABLED;
890cb475951SMarcin Krzeminski         if (s->nonvolatile_cfg & NVCFG_DUAL_IO_MASK) {
89109414144SJoe Komlodi             s->enh_volatile_cfg |= EVCFG_DUAL_IO_DISABLED;
892cb475951SMarcin Krzeminski         }
893cb475951SMarcin Krzeminski         if (s->nonvolatile_cfg & NVCFG_QUAD_IO_MASK) {
89409414144SJoe Komlodi             s->enh_volatile_cfg |= EVCFG_QUAD_IO_DISABLED;
895cb475951SMarcin Krzeminski         }
896cb475951SMarcin Krzeminski         if (!(s->nonvolatile_cfg & NVCFG_4BYTE_ADDR_MASK)) {
897cb475951SMarcin Krzeminski             s->four_bytes_address_mode = true;
898cb475951SMarcin Krzeminski         }
899cb475951SMarcin Krzeminski         if (!(s->nonvolatile_cfg & NVCFG_LOWER_SEGMENT_MASK)) {
900e02b3bf2SMarcin Krzeminski             s->ear = s->size / MAX_3BYTES_SIZE - 1;
901cb475951SMarcin Krzeminski         }
902c7cd0a6cSMarcin Krzeminski         break;
903d9cc8701SMarcin Krzeminski     case MAN_MACRONIX:
904d9cc8701SMarcin Krzeminski         s->volatile_cfg = 0x7;
905d9cc8701SMarcin Krzeminski         break;
906d9cc8701SMarcin Krzeminski     case MAN_SPANSION:
907d9cc8701SMarcin Krzeminski         s->spansion_cr1v = s->spansion_cr1nv;
908d9cc8701SMarcin Krzeminski         s->spansion_cr2v = s->spansion_cr2nv;
909d9cc8701SMarcin Krzeminski         s->spansion_cr3v = s->spansion_cr3nv;
910d9cc8701SMarcin Krzeminski         s->spansion_cr4v = s->spansion_cr4nv;
911d9cc8701SMarcin Krzeminski         s->quad_enable = extract32(s->spansion_cr1v,
912d9cc8701SMarcin Krzeminski                                    SPANSION_QUAD_CFG_POS,
913d9cc8701SMarcin Krzeminski                                    SPANSION_QUAD_CFG_LEN
914d9cc8701SMarcin Krzeminski                                    );
915d9cc8701SMarcin Krzeminski         s->four_bytes_address_mode = extract32(s->spansion_cr2v,
916d9cc8701SMarcin Krzeminski                 SPANSION_ADDR_LEN_POS,
917d9cc8701SMarcin Krzeminski                 SPANSION_ADDR_LEN_LEN
918d9cc8701SMarcin Krzeminski                 );
919d9cc8701SMarcin Krzeminski         break;
920c7cd0a6cSMarcin Krzeminski     default:
921c7cd0a6cSMarcin Krzeminski         break;
922cb475951SMarcin Krzeminski     }
923cb475951SMarcin Krzeminski 
924ccc46090SGuenter Roeck     trace_m25p80_reset_done(s);
925187c2636SMarcin Krzeminski }
926187c2636SMarcin Krzeminski 
numonyx_mode(Flash * s)92723486231SJoe Komlodi static uint8_t numonyx_mode(Flash *s)
92823486231SJoe Komlodi {
92923486231SJoe Komlodi     if (!(s->enh_volatile_cfg & EVCFG_QUAD_IO_DISABLED)) {
93023486231SJoe Komlodi         return MODE_QIO;
93123486231SJoe Komlodi     } else if (!(s->enh_volatile_cfg & EVCFG_DUAL_IO_DISABLED)) {
93223486231SJoe Komlodi         return MODE_DIO;
93323486231SJoe Komlodi     } else {
93423486231SJoe Komlodi         return MODE_STD;
93523486231SJoe Komlodi     }
93623486231SJoe Komlodi }
93723486231SJoe Komlodi 
numonyx_extract_cfg_num_dummies(Flash * s)93823af2685SJoe Komlodi static uint8_t numonyx_extract_cfg_num_dummies(Flash *s)
93923af2685SJoe Komlodi {
94023af2685SJoe Komlodi     uint8_t num_dummies;
94123af2685SJoe Komlodi     uint8_t mode;
94223af2685SJoe Komlodi     assert(get_man(s) == MAN_NUMONYX);
94323af2685SJoe Komlodi 
94423af2685SJoe Komlodi     mode = numonyx_mode(s);
94523af2685SJoe Komlodi     num_dummies = extract32(s->volatile_cfg, 4, 4);
94623af2685SJoe Komlodi 
94723af2685SJoe Komlodi     if (num_dummies == 0x0 || num_dummies == 0xf) {
94823af2685SJoe Komlodi         switch (s->cmd_in_progress) {
94923af2685SJoe Komlodi         case QIOR:
95023af2685SJoe Komlodi         case QIOR4:
95123af2685SJoe Komlodi             num_dummies = 10;
95223af2685SJoe Komlodi             break;
95323af2685SJoe Komlodi         default:
95423af2685SJoe Komlodi             num_dummies = (mode == MODE_QIO) ? 10 : 8;
95523af2685SJoe Komlodi             break;
95623af2685SJoe Komlodi         }
95723af2685SJoe Komlodi     }
95823af2685SJoe Komlodi 
95923af2685SJoe Komlodi     return num_dummies;
96023af2685SJoe Komlodi }
96123af2685SJoe Komlodi 
decode_fast_read_cmd(Flash * s)962cf6f1efeSMarcin Krzeminski static void decode_fast_read_cmd(Flash *s)
963cf6f1efeSMarcin Krzeminski {
964cf6f1efeSMarcin Krzeminski     s->needed_bytes = get_addr_length(s);
965cf6f1efeSMarcin Krzeminski     switch (get_man(s)) {
966cf6f1efeSMarcin Krzeminski     /* Dummy cycles - modeled with bytes writes instead of bits */
967aac8e46eSBin Meng     case MAN_SST:
968aac8e46eSBin Meng         s->needed_bytes += 1;
969aac8e46eSBin Meng         break;
9703830c7a4SMarcin Krzeminski     case MAN_WINBOND:
9713830c7a4SMarcin Krzeminski         s->needed_bytes += 8;
9723830c7a4SMarcin Krzeminski         break;
973cf6f1efeSMarcin Krzeminski     case MAN_NUMONYX:
97423af2685SJoe Komlodi         s->needed_bytes += numonyx_extract_cfg_num_dummies(s);
975cf6f1efeSMarcin Krzeminski         break;
976cf6f1efeSMarcin Krzeminski     case MAN_MACRONIX:
977cf6f1efeSMarcin Krzeminski         if (extract32(s->volatile_cfg, 6, 2) == 1) {
978cf6f1efeSMarcin Krzeminski             s->needed_bytes += 6;
979cf6f1efeSMarcin Krzeminski         } else {
980cf6f1efeSMarcin Krzeminski             s->needed_bytes += 8;
981cf6f1efeSMarcin Krzeminski         }
982cf6f1efeSMarcin Krzeminski         break;
983cf6f1efeSMarcin Krzeminski     case MAN_SPANSION:
984cf6f1efeSMarcin Krzeminski         s->needed_bytes += extract32(s->spansion_cr2v,
985cf6f1efeSMarcin Krzeminski                                     SPANSION_DUMMY_CLK_POS,
986cf6f1efeSMarcin Krzeminski                                     SPANSION_DUMMY_CLK_LEN
987cf6f1efeSMarcin Krzeminski                                     );
988cf6f1efeSMarcin Krzeminski         break;
98910509e10SBin Meng     case MAN_ISSI:
99010509e10SBin Meng         /*
99110509e10SBin Meng          * The Fast Read instruction code is followed by address bytes and
99210509e10SBin Meng          * dummy cycles, transmitted via the SI line.
99310509e10SBin Meng          *
99410509e10SBin Meng          * The number of dummy cycles is configurable but this is currently
99510509e10SBin Meng          * unmodeled, hence the default value 8 is used.
99610509e10SBin Meng          *
99710509e10SBin Meng          * QPI (Quad Peripheral Interface) mode has different default value
99810509e10SBin Meng          * of dummy cycles, but this is unsupported at the time being.
99910509e10SBin Meng          */
100010509e10SBin Meng         s->needed_bytes += 1;
100110509e10SBin Meng         break;
1002cf6f1efeSMarcin Krzeminski     default:
1003cf6f1efeSMarcin Krzeminski         break;
1004cf6f1efeSMarcin Krzeminski     }
1005cf6f1efeSMarcin Krzeminski     s->pos = 0;
1006cf6f1efeSMarcin Krzeminski     s->len = 0;
1007cf6f1efeSMarcin Krzeminski     s->state = STATE_COLLECTING_DATA;
1008cf6f1efeSMarcin Krzeminski }
1009cf6f1efeSMarcin Krzeminski 
decode_dio_read_cmd(Flash * s)1010cf6f1efeSMarcin Krzeminski static void decode_dio_read_cmd(Flash *s)
1011cf6f1efeSMarcin Krzeminski {
1012cf6f1efeSMarcin Krzeminski     s->needed_bytes = get_addr_length(s);
1013cf6f1efeSMarcin Krzeminski     /* Dummy cycles modeled with bytes writes instead of bits */
1014cf6f1efeSMarcin Krzeminski     switch (get_man(s)) {
1015cf6f1efeSMarcin Krzeminski     case MAN_WINBOND:
1016fe847705SMarcin Krzeminski         s->needed_bytes += WINBOND_CONTINUOUS_READ_MODE_CMD_LEN;
1017cf6f1efeSMarcin Krzeminski         break;
1018cf6f1efeSMarcin Krzeminski     case MAN_SPANSION:
1019cf6f1efeSMarcin Krzeminski         s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN;
1020cf6f1efeSMarcin Krzeminski         s->needed_bytes += extract32(s->spansion_cr2v,
1021cf6f1efeSMarcin Krzeminski                                     SPANSION_DUMMY_CLK_POS,
1022cf6f1efeSMarcin Krzeminski                                     SPANSION_DUMMY_CLK_LEN
1023cf6f1efeSMarcin Krzeminski                                     );
1024cf6f1efeSMarcin Krzeminski         break;
1025cf6f1efeSMarcin Krzeminski     case MAN_NUMONYX:
102623af2685SJoe Komlodi         s->needed_bytes += numonyx_extract_cfg_num_dummies(s);
1027cf6f1efeSMarcin Krzeminski         break;
1028cf6f1efeSMarcin Krzeminski     case MAN_MACRONIX:
1029cf6f1efeSMarcin Krzeminski         switch (extract32(s->volatile_cfg, 6, 2)) {
1030cf6f1efeSMarcin Krzeminski         case 1:
1031cf6f1efeSMarcin Krzeminski             s->needed_bytes += 6;
1032cf6f1efeSMarcin Krzeminski             break;
1033cf6f1efeSMarcin Krzeminski         case 2:
1034cf6f1efeSMarcin Krzeminski             s->needed_bytes += 8;
1035cf6f1efeSMarcin Krzeminski             break;
1036cf6f1efeSMarcin Krzeminski         default:
1037cf6f1efeSMarcin Krzeminski             s->needed_bytes += 4;
1038cf6f1efeSMarcin Krzeminski             break;
1039cf6f1efeSMarcin Krzeminski         }
1040cf6f1efeSMarcin Krzeminski         break;
104110509e10SBin Meng     case MAN_ISSI:
104210509e10SBin Meng         /*
104310509e10SBin Meng          * The Fast Read Dual I/O instruction code is followed by address bytes
104410509e10SBin Meng          * and dummy cycles, transmitted via the IO1 and IO0 line.
104510509e10SBin Meng          *
104610509e10SBin Meng          * The number of dummy cycles is configurable but this is currently
104710509e10SBin Meng          * unmodeled, hence the default value 4 is used.
104810509e10SBin Meng          */
104910509e10SBin Meng         s->needed_bytes += 1;
105010509e10SBin Meng         break;
1051cf6f1efeSMarcin Krzeminski     default:
1052cf6f1efeSMarcin Krzeminski         break;
1053cf6f1efeSMarcin Krzeminski     }
1054cf6f1efeSMarcin Krzeminski     s->pos = 0;
1055cf6f1efeSMarcin Krzeminski     s->len = 0;
1056cf6f1efeSMarcin Krzeminski     s->state = STATE_COLLECTING_DATA;
1057cf6f1efeSMarcin Krzeminski }
1058cf6f1efeSMarcin Krzeminski 
decode_qio_read_cmd(Flash * s)1059cf6f1efeSMarcin Krzeminski static void decode_qio_read_cmd(Flash *s)
1060cf6f1efeSMarcin Krzeminski {
1061cf6f1efeSMarcin Krzeminski     s->needed_bytes = get_addr_length(s);
1062cf6f1efeSMarcin Krzeminski     /* Dummy cycles modeled with bytes writes instead of bits */
1063cf6f1efeSMarcin Krzeminski     switch (get_man(s)) {
1064cf6f1efeSMarcin Krzeminski     case MAN_WINBOND:
1065fe847705SMarcin Krzeminski         s->needed_bytes += WINBOND_CONTINUOUS_READ_MODE_CMD_LEN;
1066fe847705SMarcin Krzeminski         s->needed_bytes += 4;
1067cf6f1efeSMarcin Krzeminski         break;
1068cf6f1efeSMarcin Krzeminski     case MAN_SPANSION:
1069cf6f1efeSMarcin Krzeminski         s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN;
1070cf6f1efeSMarcin Krzeminski         s->needed_bytes += extract32(s->spansion_cr2v,
1071cf6f1efeSMarcin Krzeminski                                     SPANSION_DUMMY_CLK_POS,
1072cf6f1efeSMarcin Krzeminski                                     SPANSION_DUMMY_CLK_LEN
1073cf6f1efeSMarcin Krzeminski                                     );
1074cf6f1efeSMarcin Krzeminski         break;
1075cf6f1efeSMarcin Krzeminski     case MAN_NUMONYX:
107623af2685SJoe Komlodi         s->needed_bytes += numonyx_extract_cfg_num_dummies(s);
1077cf6f1efeSMarcin Krzeminski         break;
1078cf6f1efeSMarcin Krzeminski     case MAN_MACRONIX:
1079cf6f1efeSMarcin Krzeminski         switch (extract32(s->volatile_cfg, 6, 2)) {
1080cf6f1efeSMarcin Krzeminski         case 1:
1081cf6f1efeSMarcin Krzeminski             s->needed_bytes += 4;
1082cf6f1efeSMarcin Krzeminski             break;
1083cf6f1efeSMarcin Krzeminski         case 2:
1084cf6f1efeSMarcin Krzeminski             s->needed_bytes += 8;
1085cf6f1efeSMarcin Krzeminski             break;
1086cf6f1efeSMarcin Krzeminski         default:
1087cf6f1efeSMarcin Krzeminski             s->needed_bytes += 6;
1088cf6f1efeSMarcin Krzeminski             break;
1089cf6f1efeSMarcin Krzeminski         }
1090cf6f1efeSMarcin Krzeminski         break;
109110509e10SBin Meng     case MAN_ISSI:
109210509e10SBin Meng         /*
109310509e10SBin Meng          * The Fast Read Quad I/O instruction code is followed by address bytes
109410509e10SBin Meng          * and dummy cycles, transmitted via the IO3, IO2, IO1 and IO0 line.
109510509e10SBin Meng          *
109610509e10SBin Meng          * The number of dummy cycles is configurable but this is currently
109710509e10SBin Meng          * unmodeled, hence the default value 6 is used.
109810509e10SBin Meng          *
109910509e10SBin Meng          * QPI (Quad Peripheral Interface) mode has different default value
110010509e10SBin Meng          * of dummy cycles, but this is unsupported at the time being.
110110509e10SBin Meng          */
110210509e10SBin Meng         s->needed_bytes += 3;
110310509e10SBin Meng         break;
1104cf6f1efeSMarcin Krzeminski     default:
1105cf6f1efeSMarcin Krzeminski         break;
1106cf6f1efeSMarcin Krzeminski     }
1107cf6f1efeSMarcin Krzeminski     s->pos = 0;
1108cf6f1efeSMarcin Krzeminski     s->len = 0;
1109cf6f1efeSMarcin Krzeminski     s->state = STATE_COLLECTING_DATA;
1110cf6f1efeSMarcin Krzeminski }
1111cf6f1efeSMarcin Krzeminski 
is_valid_aai_cmd(uint32_t cmd)1112465ef47aSXuzhou Cheng static bool is_valid_aai_cmd(uint32_t cmd)
1113465ef47aSXuzhou Cheng {
1114465ef47aSXuzhou Cheng     return cmd == AAI_WP || cmd == WRDI || cmd == RDSR;
1115465ef47aSXuzhou Cheng }
1116465ef47aSXuzhou Cheng 
decode_new_cmd(Flash * s,uint32_t value)111749ab747fSPaolo Bonzini static void decode_new_cmd(Flash *s, uint32_t value)
111849ab747fSPaolo Bonzini {
1119e3ba6cd6SMarcin Krzeminski     int i;
1120ccc46090SGuenter Roeck 
1121ccc46090SGuenter Roeck     s->cmd_in_progress = value;
1122ccc46090SGuenter Roeck     trace_m25p80_command_decoded(s, value);
112349ab747fSPaolo Bonzini 
1124187c2636SMarcin Krzeminski     if (value != RESET_MEMORY) {
1125187c2636SMarcin Krzeminski         s->reset_enable = false;
1126187c2636SMarcin Krzeminski     }
1127187c2636SMarcin Krzeminski 
1128465ef47aSXuzhou Cheng     if (get_man(s) == MAN_SST && s->aai_enable && !is_valid_aai_cmd(value)) {
1129465ef47aSXuzhou Cheng         qemu_log_mask(LOG_GUEST_ERROR,
1130465ef47aSXuzhou Cheng                       "M25P80: Invalid cmd within AAI programming sequence");
1131465ef47aSXuzhou Cheng     }
1132465ef47aSXuzhou Cheng 
113349ab747fSPaolo Bonzini     switch (value) {
113449ab747fSPaolo Bonzini 
113549ab747fSPaolo Bonzini     case ERASE_4K:
113663e47f6fSMarcin Krzeminski     case ERASE4_4K:
113749ab747fSPaolo Bonzini     case ERASE_32K:
113830467afeSMarcin Krzeminski     case ERASE4_32K:
113949ab747fSPaolo Bonzini     case ERASE_SECTOR:
114063e47f6fSMarcin Krzeminski     case ERASE4_SECTOR:
114149ab747fSPaolo Bonzini     case PP:
114263e47f6fSMarcin Krzeminski     case PP4:
1143f509dfeeSMarcin Krzeminski     case DIE_ERASE:
1144a87fc364SFrancisco Iglesias     case RDID_90:
1145a87fc364SFrancisco Iglesias     case RDID_AB:
1146c0f3f675SMarcin Krzeminski         s->needed_bytes = get_addr_length(s);
114749ab747fSPaolo Bonzini         s->pos = 0;
114849ab747fSPaolo Bonzini         s->len = 0;
114949ab747fSPaolo Bonzini         s->state = STATE_COLLECTING_DATA;
115049ab747fSPaolo Bonzini         break;
115123486231SJoe Komlodi     case READ:
115223486231SJoe Komlodi     case READ4:
115323486231SJoe Komlodi         if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) == MODE_STD) {
115423486231SJoe Komlodi             s->needed_bytes = get_addr_length(s);
115523486231SJoe Komlodi             s->pos = 0;
115623486231SJoe Komlodi             s->len = 0;
115723486231SJoe Komlodi             s->state = STATE_COLLECTING_DATA;
115823486231SJoe Komlodi         } else {
115923486231SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Cannot execute cmd %x in "
116023486231SJoe Komlodi                           "DIO or QIO mode\n", s->cmd_in_progress);
116123486231SJoe Komlodi         }
116223486231SJoe Komlodi         break;
116323486231SJoe Komlodi     case DPP:
116423486231SJoe Komlodi         if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) != MODE_QIO) {
116523486231SJoe Komlodi             s->needed_bytes = get_addr_length(s);
116623486231SJoe Komlodi             s->pos = 0;
116723486231SJoe Komlodi             s->len = 0;
116823486231SJoe Komlodi             s->state = STATE_COLLECTING_DATA;
116923486231SJoe Komlodi         } else {
117023486231SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Cannot execute cmd %x in "
117123486231SJoe Komlodi                           "QIO mode\n", s->cmd_in_progress);
117223486231SJoe Komlodi         }
117323486231SJoe Komlodi         break;
117423486231SJoe Komlodi     case QPP:
117523486231SJoe Komlodi     case QPP_4:
117623486231SJoe Komlodi     case PP4_4:
117723486231SJoe Komlodi         if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) != MODE_DIO) {
117823486231SJoe Komlodi             s->needed_bytes = get_addr_length(s);
117923486231SJoe Komlodi             s->pos = 0;
118023486231SJoe Komlodi             s->len = 0;
118123486231SJoe Komlodi             s->state = STATE_COLLECTING_DATA;
118223486231SJoe Komlodi         } else {
118323486231SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Cannot execute cmd %x in "
118423486231SJoe Komlodi                           "DIO mode\n", s->cmd_in_progress);
118523486231SJoe Komlodi         }
118623486231SJoe Komlodi         break;
118749ab747fSPaolo Bonzini 
118849ab747fSPaolo Bonzini     case FAST_READ:
118963e47f6fSMarcin Krzeminski     case FAST_READ4:
119023486231SJoe Komlodi         decode_fast_read_cmd(s);
119123486231SJoe Komlodi         break;
119249ab747fSPaolo Bonzini     case DOR:
119363e47f6fSMarcin Krzeminski     case DOR4:
119423486231SJoe Komlodi         if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) != MODE_QIO) {
119523486231SJoe Komlodi             decode_fast_read_cmd(s);
119623486231SJoe Komlodi         } else {
119723486231SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Cannot execute cmd %x in "
119823486231SJoe Komlodi                           "QIO mode\n", s->cmd_in_progress);
119923486231SJoe Komlodi         }
120023486231SJoe Komlodi         break;
120149ab747fSPaolo Bonzini     case QOR:
120263e47f6fSMarcin Krzeminski     case QOR4:
120323486231SJoe Komlodi         if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) != MODE_DIO) {
1204cf6f1efeSMarcin Krzeminski             decode_fast_read_cmd(s);
120523486231SJoe Komlodi         } else {
120623486231SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Cannot execute cmd %x in "
120723486231SJoe Komlodi                           "DIO mode\n", s->cmd_in_progress);
120823486231SJoe Komlodi         }
120949ab747fSPaolo Bonzini         break;
121049ab747fSPaolo Bonzini 
121149ab747fSPaolo Bonzini     case DIOR:
121263e47f6fSMarcin Krzeminski     case DIOR4:
121323486231SJoe Komlodi         if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) != MODE_QIO) {
1214cf6f1efeSMarcin Krzeminski             decode_dio_read_cmd(s);
121523486231SJoe Komlodi         } else {
121623486231SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Cannot execute cmd %x in "
121723486231SJoe Komlodi                           "QIO mode\n", s->cmd_in_progress);
121823486231SJoe Komlodi         }
121949ab747fSPaolo Bonzini         break;
122049ab747fSPaolo Bonzini 
122149ab747fSPaolo Bonzini     case QIOR:
122263e47f6fSMarcin Krzeminski     case QIOR4:
122323486231SJoe Komlodi         if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) != MODE_DIO) {
1224cf6f1efeSMarcin Krzeminski             decode_qio_read_cmd(s);
122523486231SJoe Komlodi         } else {
122623486231SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Cannot execute cmd %x in "
122723486231SJoe Komlodi                           "DIO mode\n", s->cmd_in_progress);
122823486231SJoe Komlodi         }
122949ab747fSPaolo Bonzini         break;
123049ab747fSPaolo Bonzini 
123149ab747fSPaolo Bonzini     case WRSR:
12322fa22a0fSIris Chen         /*
12332fa22a0fSIris Chen          * If WP# is low and status_register_write_disabled is high,
12342fa22a0fSIris Chen          * status register writes are disabled.
12352fa22a0fSIris Chen          * This is also called "hardware protected mode" (HPM). All other
12362fa22a0fSIris Chen          * combinations of the two states are called "software protected mode"
12372fa22a0fSIris Chen          * (SPM), and status register writes are permitted.
12382fa22a0fSIris Chen          */
12392fa22a0fSIris Chen         if ((s->wp_level == 0 && s->status_register_write_disabled)
12402fa22a0fSIris Chen             || !s->write_enable) {
12412fa22a0fSIris Chen             qemu_log_mask(LOG_GUEST_ERROR,
12422fa22a0fSIris Chen                           "M25P80: Status register write is disabled!\n");
12432fa22a0fSIris Chen             break;
12442fa22a0fSIris Chen         }
12452fa22a0fSIris Chen 
12467a69c100SMarcin Krzeminski         switch (get_man(s)) {
12477a69c100SMarcin Krzeminski         case MAN_SPANSION:
12487a69c100SMarcin Krzeminski             s->needed_bytes = 2;
124949ab747fSPaolo Bonzini             s->state = STATE_COLLECTING_DATA;
12507a69c100SMarcin Krzeminski             break;
12517a69c100SMarcin Krzeminski         case MAN_MACRONIX:
12527a69c100SMarcin Krzeminski             s->needed_bytes = 2;
12537a69c100SMarcin Krzeminski             s->state = STATE_COLLECTING_VAR_LEN_DATA;
12547a69c100SMarcin Krzeminski             break;
12557a69c100SMarcin Krzeminski         default:
12567a69c100SMarcin Krzeminski             s->needed_bytes = 1;
12577a69c100SMarcin Krzeminski             s->state = STATE_COLLECTING_DATA;
12587a69c100SMarcin Krzeminski         }
12597a69c100SMarcin Krzeminski         s->pos = 0;
126049ab747fSPaolo Bonzini         break;
126149ab747fSPaolo Bonzini 
126249ab747fSPaolo Bonzini     case WRDI:
126349ab747fSPaolo Bonzini         s->write_enable = false;
1264465ef47aSXuzhou Cheng         if (get_man(s) == MAN_SST) {
1265465ef47aSXuzhou Cheng             s->aai_enable = false;
1266465ef47aSXuzhou Cheng         }
126749ab747fSPaolo Bonzini         break;
126849ab747fSPaolo Bonzini     case WREN:
126949ab747fSPaolo Bonzini         s->write_enable = true;
127049ab747fSPaolo Bonzini         break;
127149ab747fSPaolo Bonzini 
127249ab747fSPaolo Bonzini     case RDSR:
127349ab747fSPaolo Bonzini         s->data[0] = (!!s->write_enable) << 1;
12742fa22a0fSIris Chen         s->data[0] |= (!!s->status_register_write_disabled) << 7;
12752113a128SIris Chen         s->data[0] |= (!!s->block_protect0) << 2;
12762113a128SIris Chen         s->data[0] |= (!!s->block_protect1) << 3;
12772113a128SIris Chen         s->data[0] |= (!!s->block_protect2) << 4;
12782113a128SIris Chen         if (s->pi->flags & HAS_SR_TB) {
12792113a128SIris Chen             s->data[0] |= (!!s->top_bottom_bit) << 5;
12802113a128SIris Chen         }
12812113a128SIris Chen         if (s->pi->flags & HAS_SR_BP3_BIT6) {
12822113a128SIris Chen             s->data[0] |= (!!s->block_protect3) << 6;
12832113a128SIris Chen         }
12842fa22a0fSIris Chen 
128510509e10SBin Meng         if (get_man(s) == MAN_MACRONIX || get_man(s) == MAN_ISSI) {
12867a69c100SMarcin Krzeminski             s->data[0] |= (!!s->quad_enable) << 6;
12877a69c100SMarcin Krzeminski         }
1288465ef47aSXuzhou Cheng         if (get_man(s) == MAN_SST) {
1289465ef47aSXuzhou Cheng             s->data[0] |= (!!s->aai_enable) << 6;
1290465ef47aSXuzhou Cheng         }
1291465ef47aSXuzhou Cheng 
129249ab747fSPaolo Bonzini         s->pos = 0;
129349ab747fSPaolo Bonzini         s->len = 1;
12940add925fSFrancisco Iglesias         s->data_read_loop = true;
129549ab747fSPaolo Bonzini         s->state = STATE_READING_DATA;
129649ab747fSPaolo Bonzini         break;
129749ab747fSPaolo Bonzini 
12989fbaa364SMarcin Krzeminski     case READ_FSR:
12999fbaa364SMarcin Krzeminski         s->data[0] = FSR_FLASH_READY;
13009fbaa364SMarcin Krzeminski         if (s->four_bytes_address_mode) {
13019fbaa364SMarcin Krzeminski             s->data[0] |= FSR_4BYTE_ADDR_MODE_ENABLED;
13029fbaa364SMarcin Krzeminski         }
13039fbaa364SMarcin Krzeminski         s->pos = 0;
13049fbaa364SMarcin Krzeminski         s->len = 1;
13050add925fSFrancisco Iglesias         s->data_read_loop = true;
13069fbaa364SMarcin Krzeminski         s->state = STATE_READING_DATA;
13079fbaa364SMarcin Krzeminski         break;
13089fbaa364SMarcin Krzeminski 
130949ab747fSPaolo Bonzini     case JEDEC_READ:
131023486231SJoe Komlodi         if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) == MODE_STD) {
1311ccc46090SGuenter Roeck             trace_m25p80_populated_jedec(s);
1312e3ba6cd6SMarcin Krzeminski             for (i = 0; i < s->pi->id_len; i++) {
1313e3ba6cd6SMarcin Krzeminski                 s->data[i] = s->pi->id[i];
131449ab747fSPaolo Bonzini             }
1315f3ee222fSGuenter Roeck             for (; i < SPI_NOR_MAX_ID_LEN; i++) {
1316f3ee222fSGuenter Roeck                 s->data[i] = 0;
1317f3ee222fSGuenter Roeck             }
1318e3ba6cd6SMarcin Krzeminski 
1319f3ee222fSGuenter Roeck             s->len = SPI_NOR_MAX_ID_LEN;
132049ab747fSPaolo Bonzini             s->pos = 0;
132149ab747fSPaolo Bonzini             s->state = STATE_READING_DATA;
132223486231SJoe Komlodi         } else {
132323486231SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Cannot execute JEDEC read "
132423486231SJoe Komlodi                           "in DIO or QIO mode\n");
132523486231SJoe Komlodi         }
132649ab747fSPaolo Bonzini         break;
132749ab747fSPaolo Bonzini 
13287a69c100SMarcin Krzeminski     case RDCR:
13297a69c100SMarcin Krzeminski         s->data[0] = s->volatile_cfg & 0xFF;
13307a69c100SMarcin Krzeminski         s->data[0] |= (!!s->four_bytes_address_mode) << 5;
13317a69c100SMarcin Krzeminski         s->pos = 0;
13327a69c100SMarcin Krzeminski         s->len = 1;
13337a69c100SMarcin Krzeminski         s->state = STATE_READING_DATA;
13347a69c100SMarcin Krzeminski         break;
13357a69c100SMarcin Krzeminski 
13360f589782SFrancisco Iglesias     case BULK_ERASE_60:
133749ab747fSPaolo Bonzini     case BULK_ERASE:
133849ab747fSPaolo Bonzini         if (s->write_enable) {
1339ccc46090SGuenter Roeck             trace_m25p80_chip_erase(s);
134049ab747fSPaolo Bonzini             flash_erase(s, 0, BULK_ERASE);
134149ab747fSPaolo Bonzini         } else {
1342e9711b4dSPeter Crosthwaite             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: chip erase with write "
1343e9711b4dSPeter Crosthwaite                           "protect!\n");
134449ab747fSPaolo Bonzini         }
134549ab747fSPaolo Bonzini         break;
134649ab747fSPaolo Bonzini     case NOP:
134749ab747fSPaolo Bonzini         break;
1348c0f3f675SMarcin Krzeminski     case EN_4BYTE_ADDR:
1349c0f3f675SMarcin Krzeminski         s->four_bytes_address_mode = true;
1350c0f3f675SMarcin Krzeminski         break;
1351c0f3f675SMarcin Krzeminski     case EX_4BYTE_ADDR:
1352c0f3f675SMarcin Krzeminski         s->four_bytes_address_mode = false;
1353c0f3f675SMarcin Krzeminski         break;
13540f589782SFrancisco Iglesias     case BRRD:
1355d8a29a7aSMarcin Krzeminski     case EXTEND_ADDR_READ:
1356d8a29a7aSMarcin Krzeminski         s->data[0] = s->ear;
1357d8a29a7aSMarcin Krzeminski         s->pos = 0;
1358d8a29a7aSMarcin Krzeminski         s->len = 1;
1359d8a29a7aSMarcin Krzeminski         s->state = STATE_READING_DATA;
1360d8a29a7aSMarcin Krzeminski         break;
13610f589782SFrancisco Iglesias     case BRWR:
1362d8a29a7aSMarcin Krzeminski     case EXTEND_ADDR_WRITE:
1363d8a29a7aSMarcin Krzeminski         if (s->write_enable) {
1364d8a29a7aSMarcin Krzeminski             s->needed_bytes = 1;
1365d8a29a7aSMarcin Krzeminski             s->pos = 0;
1366d8a29a7aSMarcin Krzeminski             s->len = 0;
1367d8a29a7aSMarcin Krzeminski             s->state = STATE_COLLECTING_DATA;
1368d8a29a7aSMarcin Krzeminski         }
1369d8a29a7aSMarcin Krzeminski         break;
1370cb475951SMarcin Krzeminski     case RNVCR:
1371cb475951SMarcin Krzeminski         s->data[0] = s->nonvolatile_cfg & 0xFF;
1372cb475951SMarcin Krzeminski         s->data[1] = (s->nonvolatile_cfg >> 8) & 0xFF;
1373cb475951SMarcin Krzeminski         s->pos = 0;
1374cb475951SMarcin Krzeminski         s->len = 2;
1375cb475951SMarcin Krzeminski         s->state = STATE_READING_DATA;
1376cb475951SMarcin Krzeminski         break;
1377cb475951SMarcin Krzeminski     case WNVCR:
13787a69c100SMarcin Krzeminski         if (s->write_enable && get_man(s) == MAN_NUMONYX) {
1379cb475951SMarcin Krzeminski             s->needed_bytes = 2;
1380cb475951SMarcin Krzeminski             s->pos = 0;
1381cb475951SMarcin Krzeminski             s->len = 0;
1382cb475951SMarcin Krzeminski             s->state = STATE_COLLECTING_DATA;
1383cb475951SMarcin Krzeminski         }
1384cb475951SMarcin Krzeminski         break;
1385cb475951SMarcin Krzeminski     case RVCR:
1386cb475951SMarcin Krzeminski         s->data[0] = s->volatile_cfg & 0xFF;
1387cb475951SMarcin Krzeminski         s->pos = 0;
1388cb475951SMarcin Krzeminski         s->len = 1;
1389cb475951SMarcin Krzeminski         s->state = STATE_READING_DATA;
1390cb475951SMarcin Krzeminski         break;
1391cb475951SMarcin Krzeminski     case WVCR:
1392cb475951SMarcin Krzeminski         if (s->write_enable) {
1393cb475951SMarcin Krzeminski             s->needed_bytes = 1;
1394cb475951SMarcin Krzeminski             s->pos = 0;
1395cb475951SMarcin Krzeminski             s->len = 0;
1396cb475951SMarcin Krzeminski             s->state = STATE_COLLECTING_DATA;
1397cb475951SMarcin Krzeminski         }
1398cb475951SMarcin Krzeminski         break;
1399cb475951SMarcin Krzeminski     case REVCR:
1400cb475951SMarcin Krzeminski         s->data[0] = s->enh_volatile_cfg & 0xFF;
1401cb475951SMarcin Krzeminski         s->pos = 0;
1402cb475951SMarcin Krzeminski         s->len = 1;
1403cb475951SMarcin Krzeminski         s->state = STATE_READING_DATA;
1404cb475951SMarcin Krzeminski         break;
1405cb475951SMarcin Krzeminski     case WEVCR:
1406cb475951SMarcin Krzeminski         if (s->write_enable) {
1407cb475951SMarcin Krzeminski             s->needed_bytes = 1;
1408cb475951SMarcin Krzeminski             s->pos = 0;
1409cb475951SMarcin Krzeminski             s->len = 0;
1410cb475951SMarcin Krzeminski             s->state = STATE_COLLECTING_DATA;
1411cb475951SMarcin Krzeminski         }
1412cb475951SMarcin Krzeminski         break;
1413187c2636SMarcin Krzeminski     case RESET_ENABLE:
1414187c2636SMarcin Krzeminski         s->reset_enable = true;
1415187c2636SMarcin Krzeminski         break;
1416187c2636SMarcin Krzeminski     case RESET_MEMORY:
1417187c2636SMarcin Krzeminski         if (s->reset_enable) {
1418187c2636SMarcin Krzeminski             reset_memory(s);
1419187c2636SMarcin Krzeminski         }
1420187c2636SMarcin Krzeminski         break;
14217a69c100SMarcin Krzeminski     case RDCR_EQIO:
14227a69c100SMarcin Krzeminski         switch (get_man(s)) {
14237a69c100SMarcin Krzeminski         case MAN_SPANSION:
14247a69c100SMarcin Krzeminski             s->data[0] = (!!s->quad_enable) << 1;
14257a69c100SMarcin Krzeminski             s->pos = 0;
14267a69c100SMarcin Krzeminski             s->len = 1;
14277a69c100SMarcin Krzeminski             s->state = STATE_READING_DATA;
14287a69c100SMarcin Krzeminski             break;
14297a69c100SMarcin Krzeminski         case MAN_MACRONIX:
14307a69c100SMarcin Krzeminski             s->quad_enable = true;
14317a69c100SMarcin Krzeminski             break;
14327a69c100SMarcin Krzeminski         default:
14337a69c100SMarcin Krzeminski             break;
14347a69c100SMarcin Krzeminski         }
14357a69c100SMarcin Krzeminski         break;
14367a69c100SMarcin Krzeminski     case RSTQIO:
14377a69c100SMarcin Krzeminski         s->quad_enable = false;
14387a69c100SMarcin Krzeminski         break;
1439465ef47aSXuzhou Cheng     case AAI_WP:
1440465ef47aSXuzhou Cheng         if (get_man(s) == MAN_SST) {
1441465ef47aSXuzhou Cheng             if (s->write_enable) {
1442465ef47aSXuzhou Cheng                 if (s->aai_enable) {
1443465ef47aSXuzhou Cheng                     s->state = STATE_PAGE_PROGRAM;
1444465ef47aSXuzhou Cheng                 } else {
1445465ef47aSXuzhou Cheng                     s->aai_enable = true;
1446465ef47aSXuzhou Cheng                     s->needed_bytes = get_addr_length(s);
1447465ef47aSXuzhou Cheng                     s->state = STATE_COLLECTING_DATA;
1448465ef47aSXuzhou Cheng                 }
1449465ef47aSXuzhou Cheng             } else {
1450465ef47aSXuzhou Cheng                 qemu_log_mask(LOG_GUEST_ERROR,
1451465ef47aSXuzhou Cheng                               "M25P80: AAI_WP with write protect\n");
1452465ef47aSXuzhou Cheng             }
1453465ef47aSXuzhou Cheng         } else {
1454465ef47aSXuzhou Cheng             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value);
1455465ef47aSXuzhou Cheng         }
1456465ef47aSXuzhou Cheng         break;
14572389bcc2SCédric Le Goater     case RDSFDP:
14582389bcc2SCédric Le Goater         if (s->pi->sfdp_read) {
14592389bcc2SCédric Le Goater             s->needed_bytes = get_addr_length(s) + 1; /* SFDP addr + dummy */
14602389bcc2SCédric Le Goater             s->pos = 0;
14612389bcc2SCédric Le Goater             s->len = 0;
14622389bcc2SCédric Le Goater             s->state = STATE_COLLECTING_DATA;
14632389bcc2SCédric Le Goater             break;
14642389bcc2SCédric Le Goater         }
14652389bcc2SCédric Le Goater         /* Fallthrough */
14662389bcc2SCédric Le Goater 
146749ab747fSPaolo Bonzini     default:
14689c85bcd8SGuenter Roeck         s->pos = 0;
14699c85bcd8SGuenter Roeck         s->len = 1;
14709c85bcd8SGuenter Roeck         s->state = STATE_READING_DATA;
14719c85bcd8SGuenter Roeck         s->data_read_loop = true;
14729c85bcd8SGuenter Roeck         s->data[0] = 0;
1473e9711b4dSPeter Crosthwaite         qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value);
147449ab747fSPaolo Bonzini         break;
147549ab747fSPaolo Bonzini     }
147649ab747fSPaolo Bonzini }
147749ab747fSPaolo Bonzini 
m25p80_cs(SSIPeripheral * ss,bool select)1478ec7e429bSPhilippe Mathieu-Daudé static int m25p80_cs(SSIPeripheral *ss, bool select)
147949ab747fSPaolo Bonzini {
1480cdccf7d7SPeter Crosthwaite     Flash *s = M25P80(ss);
148149ab747fSPaolo Bonzini 
148249ab747fSPaolo Bonzini     if (select) {
14839964674eSMarcin Krzeminski         if (s->state == STATE_COLLECTING_VAR_LEN_DATA) {
14849964674eSMarcin Krzeminski             complete_collecting_data(s);
14859964674eSMarcin Krzeminski         }
148649ab747fSPaolo Bonzini         s->len = 0;
148749ab747fSPaolo Bonzini         s->pos = 0;
148849ab747fSPaolo Bonzini         s->state = STATE_IDLE;
148949ab747fSPaolo Bonzini         flash_sync_dirty(s, -1);
14900add925fSFrancisco Iglesias         s->data_read_loop = false;
149149ab747fSPaolo Bonzini     }
149249ab747fSPaolo Bonzini 
1493ccc46090SGuenter Roeck     trace_m25p80_select(s, select ? "de" : "");
149449ab747fSPaolo Bonzini 
149549ab747fSPaolo Bonzini     return 0;
149649ab747fSPaolo Bonzini }
149749ab747fSPaolo Bonzini 
m25p80_transfer8(SSIPeripheral * ss,uint32_t tx)1498ec7e429bSPhilippe Mathieu-Daudé static uint32_t m25p80_transfer8(SSIPeripheral *ss, uint32_t tx)
149949ab747fSPaolo Bonzini {
1500cdccf7d7SPeter Crosthwaite     Flash *s = M25P80(ss);
150149ab747fSPaolo Bonzini     uint32_t r = 0;
150249ab747fSPaolo Bonzini 
1503ccc46090SGuenter Roeck     trace_m25p80_transfer(s, s->state, s->len, s->needed_bytes, s->pos,
1504ccc46090SGuenter Roeck                           s->cur_addr, (uint8_t)tx);
1505ccc46090SGuenter Roeck 
150649ab747fSPaolo Bonzini     switch (s->state) {
150749ab747fSPaolo Bonzini 
150849ab747fSPaolo Bonzini     case STATE_PAGE_PROGRAM:
1509ccc46090SGuenter Roeck         trace_m25p80_page_program(s, s->cur_addr, (uint8_t)tx);
151049ab747fSPaolo Bonzini         flash_write8(s, s->cur_addr, (uint8_t)tx);
1511b68cb060SPaolo Bonzini         s->cur_addr = (s->cur_addr + 1) & (s->size - 1);
1512465ef47aSXuzhou Cheng 
1513465ef47aSXuzhou Cheng         if (get_man(s) == MAN_SST && s->aai_enable && s->cur_addr == 0) {
1514465ef47aSXuzhou Cheng             /*
1515465ef47aSXuzhou Cheng              * There is no wrap mode during AAI programming once the highest
1516465ef47aSXuzhou Cheng              * unprotected memory address is reached. The Write-Enable-Latch
1517465ef47aSXuzhou Cheng              * bit is automatically reset, and AAI programming mode aborts.
1518465ef47aSXuzhou Cheng              */
1519465ef47aSXuzhou Cheng             s->write_enable = false;
1520465ef47aSXuzhou Cheng             s->aai_enable = false;
1521465ef47aSXuzhou Cheng         }
1522465ef47aSXuzhou Cheng 
152349ab747fSPaolo Bonzini         break;
152449ab747fSPaolo Bonzini 
152549ab747fSPaolo Bonzini     case STATE_READ:
152649ab747fSPaolo Bonzini         r = s->storage[s->cur_addr];
1527ccc46090SGuenter Roeck         trace_m25p80_read_byte(s, s->cur_addr, (uint8_t)r);
1528b68cb060SPaolo Bonzini         s->cur_addr = (s->cur_addr + 1) & (s->size - 1);
152949ab747fSPaolo Bonzini         break;
153049ab747fSPaolo Bonzini 
153149ab747fSPaolo Bonzini     case STATE_COLLECTING_DATA:
15329964674eSMarcin Krzeminski     case STATE_COLLECTING_VAR_LEN_DATA:
153324cb2e0dSJean-Christophe Dubois 
153424cb2e0dSJean-Christophe Dubois         if (s->len >= M25P80_INTERNAL_DATA_BUFFER_SZ) {
153524cb2e0dSJean-Christophe Dubois             qemu_log_mask(LOG_GUEST_ERROR,
153624cb2e0dSJean-Christophe Dubois                           "M25P80: Write overrun internal data buffer. "
153724cb2e0dSJean-Christophe Dubois                           "SPI controller (QEMU emulator or guest driver) "
153824cb2e0dSJean-Christophe Dubois                           "is misbehaving\n");
153924cb2e0dSJean-Christophe Dubois             s->len = s->pos = 0;
154024cb2e0dSJean-Christophe Dubois             s->state = STATE_IDLE;
154124cb2e0dSJean-Christophe Dubois             break;
154224cb2e0dSJean-Christophe Dubois         }
154324cb2e0dSJean-Christophe Dubois 
154449ab747fSPaolo Bonzini         s->data[s->len] = (uint8_t)tx;
154549ab747fSPaolo Bonzini         s->len++;
154649ab747fSPaolo Bonzini 
154749ab747fSPaolo Bonzini         if (s->len == s->needed_bytes) {
154849ab747fSPaolo Bonzini             complete_collecting_data(s);
154949ab747fSPaolo Bonzini         }
155049ab747fSPaolo Bonzini         break;
155149ab747fSPaolo Bonzini 
155249ab747fSPaolo Bonzini     case STATE_READING_DATA:
155324cb2e0dSJean-Christophe Dubois 
155424cb2e0dSJean-Christophe Dubois         if (s->pos >= M25P80_INTERNAL_DATA_BUFFER_SZ) {
155524cb2e0dSJean-Christophe Dubois             qemu_log_mask(LOG_GUEST_ERROR,
155624cb2e0dSJean-Christophe Dubois                           "M25P80: Read overrun internal data buffer. "
155724cb2e0dSJean-Christophe Dubois                           "SPI controller (QEMU emulator or guest driver) "
155824cb2e0dSJean-Christophe Dubois                           "is misbehaving\n");
155924cb2e0dSJean-Christophe Dubois             s->len = s->pos = 0;
156024cb2e0dSJean-Christophe Dubois             s->state = STATE_IDLE;
156124cb2e0dSJean-Christophe Dubois             break;
156224cb2e0dSJean-Christophe Dubois         }
156324cb2e0dSJean-Christophe Dubois 
156449ab747fSPaolo Bonzini         r = s->data[s->pos];
1565ccc46090SGuenter Roeck         trace_m25p80_read_data(s, s->pos, (uint8_t)r);
156649ab747fSPaolo Bonzini         s->pos++;
156749ab747fSPaolo Bonzini         if (s->pos == s->len) {
156849ab747fSPaolo Bonzini             s->pos = 0;
15690add925fSFrancisco Iglesias             if (!s->data_read_loop) {
157049ab747fSPaolo Bonzini                 s->state = STATE_IDLE;
157149ab747fSPaolo Bonzini             }
15720add925fSFrancisco Iglesias         }
157349ab747fSPaolo Bonzini         break;
15742389bcc2SCédric Le Goater     case STATE_READING_SFDP:
15752389bcc2SCédric Le Goater         assert(s->pi->sfdp_read);
15762389bcc2SCédric Le Goater         r = s->pi->sfdp_read(s->cur_addr);
15772389bcc2SCédric Le Goater         trace_m25p80_read_sfdp(s, s->cur_addr, (uint8_t)r);
15782389bcc2SCédric Le Goater         s->cur_addr = (s->cur_addr + 1) & (M25P80_SFDP_MAX_SIZE - 1);
15792389bcc2SCédric Le Goater         break;
158049ab747fSPaolo Bonzini 
158149ab747fSPaolo Bonzini     default:
158249ab747fSPaolo Bonzini     case STATE_IDLE:
158349ab747fSPaolo Bonzini         decode_new_cmd(s, (uint8_t)tx);
158449ab747fSPaolo Bonzini         break;
158549ab747fSPaolo Bonzini     }
158649ab747fSPaolo Bonzini 
158749ab747fSPaolo Bonzini     return r;
158849ab747fSPaolo Bonzini }
158949ab747fSPaolo Bonzini 
m25p80_write_protect_pin_irq_handler(void * opaque,int n,int level)15902fa22a0fSIris Chen static void m25p80_write_protect_pin_irq_handler(void *opaque, int n, int level)
15912fa22a0fSIris Chen {
15922fa22a0fSIris Chen     Flash *s = M25P80(opaque);
15932fa22a0fSIris Chen     /* WP# is just a single pin. */
15942fa22a0fSIris Chen     assert(n == 0);
15952fa22a0fSIris Chen     s->wp_level = !!level;
15962fa22a0fSIris Chen }
15972fa22a0fSIris Chen 
m25p80_realize(SSIPeripheral * ss,Error ** errp)1598ec7e429bSPhilippe Mathieu-Daudé static void m25p80_realize(SSIPeripheral *ss, Error **errp)
159949ab747fSPaolo Bonzini {
1600cdccf7d7SPeter Crosthwaite     Flash *s = M25P80(ss);
160149ab747fSPaolo Bonzini     M25P80Class *mc = M25P80_GET_CLASS(s);
1602a17c17a2SKevin Wolf     int ret;
160349ab747fSPaolo Bonzini 
160449ab747fSPaolo Bonzini     s->pi = mc->pi;
160549ab747fSPaolo Bonzini 
160649ab747fSPaolo Bonzini     s->size = s->pi->sector_size * s->pi->n_sectors;
160749ab747fSPaolo Bonzini     s->dirty_page = -1;
160849ab747fSPaolo Bonzini 
160973bce518SPaolo Bonzini     if (s->blk) {
1610a17c17a2SKevin Wolf         uint64_t perm = BLK_PERM_CONSISTENT_READ |
161186b1cf32SKevin Wolf                         (blk_supports_write_perm(s->blk) ? BLK_PERM_WRITE : 0);
1612a17c17a2SKevin Wolf         ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp);
1613a17c17a2SKevin Wolf         if (ret < 0) {
1614a17c17a2SKevin Wolf             return;
1615a17c17a2SKevin Wolf         }
1616a17c17a2SKevin Wolf 
1617ccc46090SGuenter Roeck         trace_m25p80_binding(s);
1618c485cf9cSStefan Hajnoczi         s->storage = blk_blockalign(s->blk, s->size);
1619c485cf9cSStefan Hajnoczi 
162011aeb4b8SCédric Le Goater         if (!blk_check_size_and_read_all(s->blk, s->storage, s->size, errp)) {
16217673bb4cSCédric Le Goater             return;
162249ab747fSPaolo Bonzini         }
162349ab747fSPaolo Bonzini     } else {
1624ccc46090SGuenter Roeck         trace_m25p80_binding_no_bdrv(s);
1625c485cf9cSStefan Hajnoczi         s->storage = blk_blockalign(NULL, s->size);
162649ab747fSPaolo Bonzini         memset(s->storage, 0xFF, s->size);
162749ab747fSPaolo Bonzini     }
16282fa22a0fSIris Chen 
16292fa22a0fSIris Chen     qdev_init_gpio_in_named(DEVICE(s),
16302fa22a0fSIris Chen                             m25p80_write_protect_pin_irq_handler, "WP#", 1);
163149ab747fSPaolo Bonzini }
163249ab747fSPaolo Bonzini 
m25p80_reset(DeviceState * d)1633187c2636SMarcin Krzeminski static void m25p80_reset(DeviceState *d)
1634187c2636SMarcin Krzeminski {
1635187c2636SMarcin Krzeminski     Flash *s = M25P80(d);
1636187c2636SMarcin Krzeminski 
16372fa22a0fSIris Chen     s->wp_level = true;
16382fa22a0fSIris Chen     s->status_register_write_disabled = false;
16392113a128SIris Chen     s->block_protect0 = false;
16402113a128SIris Chen     s->block_protect1 = false;
16412113a128SIris Chen     s->block_protect2 = false;
16422113a128SIris Chen     s->block_protect3 = false;
16432113a128SIris Chen     s->top_bottom_bit = false;
16442fa22a0fSIris Chen 
1645187c2636SMarcin Krzeminski     reset_memory(s);
1646187c2636SMarcin Krzeminski }
1647187c2636SMarcin Krzeminski 
m25p80_pre_save(void * opaque)164844b1ff31SDr. David Alan Gilbert static int m25p80_pre_save(void *opaque)
164949ab747fSPaolo Bonzini {
165049ab747fSPaolo Bonzini     flash_sync_dirty((Flash *)opaque, -1);
165144b1ff31SDr. David Alan Gilbert 
165244b1ff31SDr. David Alan Gilbert     return 0;
165349ab747fSPaolo Bonzini }
165449ab747fSPaolo Bonzini 
1655cb475951SMarcin Krzeminski static Property m25p80_properties[] = {
1656d9cc8701SMarcin Krzeminski     /* This is default value for Micron flash */
1657188052a1SIris Chen     DEFINE_PROP_BOOL("write-enable", Flash, write_enable, false),
1658cb475951SMarcin Krzeminski     DEFINE_PROP_UINT32("nonvolatile-cfg", Flash, nonvolatile_cfg, 0x8FFF),
1659d9cc8701SMarcin Krzeminski     DEFINE_PROP_UINT8("spansion-cr1nv", Flash, spansion_cr1nv, 0x0),
1660d9cc8701SMarcin Krzeminski     DEFINE_PROP_UINT8("spansion-cr2nv", Flash, spansion_cr2nv, 0x8),
1661d9cc8701SMarcin Krzeminski     DEFINE_PROP_UINT8("spansion-cr3nv", Flash, spansion_cr3nv, 0x2),
1662d9cc8701SMarcin Krzeminski     DEFINE_PROP_UINT8("spansion-cr4nv", Flash, spansion_cr4nv, 0x10),
166373bce518SPaolo Bonzini     DEFINE_PROP_DRIVE("drive", Flash, blk),
1664cb475951SMarcin Krzeminski     DEFINE_PROP_END_OF_LIST(),
1665cb475951SMarcin Krzeminski };
1666cb475951SMarcin Krzeminski 
m25p80_pre_load(void * opaque)16670add925fSFrancisco Iglesias static int m25p80_pre_load(void *opaque)
16680add925fSFrancisco Iglesias {
16690add925fSFrancisco Iglesias     Flash *s = (Flash *)opaque;
16700add925fSFrancisco Iglesias 
16710add925fSFrancisco Iglesias     s->data_read_loop = false;
16720add925fSFrancisco Iglesias     return 0;
16730add925fSFrancisco Iglesias }
16740add925fSFrancisco Iglesias 
m25p80_data_read_loop_needed(void * opaque)16750add925fSFrancisco Iglesias static bool m25p80_data_read_loop_needed(void *opaque)
16760add925fSFrancisco Iglesias {
16770add925fSFrancisco Iglesias     Flash *s = (Flash *)opaque;
16780add925fSFrancisco Iglesias 
16790add925fSFrancisco Iglesias     return s->data_read_loop;
16800add925fSFrancisco Iglesias }
16810add925fSFrancisco Iglesias 
16820add925fSFrancisco Iglesias static const VMStateDescription vmstate_m25p80_data_read_loop = {
16830add925fSFrancisco Iglesias     .name = "m25p80/data_read_loop",
16840add925fSFrancisco Iglesias     .version_id = 1,
16850add925fSFrancisco Iglesias     .minimum_version_id = 1,
16860add925fSFrancisco Iglesias     .needed = m25p80_data_read_loop_needed,
16870add925fSFrancisco Iglesias     .fields = (VMStateField[]) {
16880add925fSFrancisco Iglesias         VMSTATE_BOOL(data_read_loop, Flash),
16890add925fSFrancisco Iglesias         VMSTATE_END_OF_LIST()
16900add925fSFrancisco Iglesias     }
16910add925fSFrancisco Iglesias };
16920add925fSFrancisco Iglesias 
m25p80_aai_enable_needed(void * opaque)1693465ef47aSXuzhou Cheng static bool m25p80_aai_enable_needed(void *opaque)
1694465ef47aSXuzhou Cheng {
1695465ef47aSXuzhou Cheng     Flash *s = (Flash *)opaque;
1696465ef47aSXuzhou Cheng 
1697465ef47aSXuzhou Cheng     return s->aai_enable;
1698465ef47aSXuzhou Cheng }
1699465ef47aSXuzhou Cheng 
1700465ef47aSXuzhou Cheng static const VMStateDescription vmstate_m25p80_aai_enable = {
1701465ef47aSXuzhou Cheng     .name = "m25p80/aai_enable",
1702465ef47aSXuzhou Cheng     .version_id = 1,
1703465ef47aSXuzhou Cheng     .minimum_version_id = 1,
1704465ef47aSXuzhou Cheng     .needed = m25p80_aai_enable_needed,
1705465ef47aSXuzhou Cheng     .fields = (VMStateField[]) {
1706465ef47aSXuzhou Cheng         VMSTATE_BOOL(aai_enable, Flash),
1707465ef47aSXuzhou Cheng         VMSTATE_END_OF_LIST()
1708465ef47aSXuzhou Cheng     }
1709465ef47aSXuzhou Cheng };
1710465ef47aSXuzhou Cheng 
m25p80_wp_level_srwd_needed(void * opaque)17112fa22a0fSIris Chen static bool m25p80_wp_level_srwd_needed(void *opaque)
17122fa22a0fSIris Chen {
17132fa22a0fSIris Chen     Flash *s = (Flash *)opaque;
17142fa22a0fSIris Chen 
17152fa22a0fSIris Chen     return !s->wp_level || s->status_register_write_disabled;
17162fa22a0fSIris Chen }
17172fa22a0fSIris Chen 
17182fa22a0fSIris Chen static const VMStateDescription vmstate_m25p80_write_protect = {
17192fa22a0fSIris Chen     .name = "m25p80/write_protect",
17202fa22a0fSIris Chen     .version_id = 1,
17212fa22a0fSIris Chen     .minimum_version_id = 1,
17222fa22a0fSIris Chen     .needed = m25p80_wp_level_srwd_needed,
17232fa22a0fSIris Chen     .fields = (VMStateField[]) {
17242fa22a0fSIris Chen         VMSTATE_BOOL(wp_level, Flash),
17252fa22a0fSIris Chen         VMSTATE_BOOL(status_register_write_disabled, Flash),
17262fa22a0fSIris Chen         VMSTATE_END_OF_LIST()
17272fa22a0fSIris Chen     }
17282fa22a0fSIris Chen };
17292fa22a0fSIris Chen 
m25p80_block_protect_needed(void * opaque)17302113a128SIris Chen static bool m25p80_block_protect_needed(void *opaque)
17312113a128SIris Chen {
17322113a128SIris Chen     Flash *s = (Flash *)opaque;
17332113a128SIris Chen 
17342113a128SIris Chen     return s->block_protect0 ||
17352113a128SIris Chen            s->block_protect1 ||
17362113a128SIris Chen            s->block_protect2 ||
17372113a128SIris Chen            s->block_protect3 ||
17382113a128SIris Chen            s->top_bottom_bit;
17392113a128SIris Chen }
17402113a128SIris Chen 
17412113a128SIris Chen static const VMStateDescription vmstate_m25p80_block_protect = {
17422113a128SIris Chen     .name = "m25p80/block_protect",
17432113a128SIris Chen     .version_id = 1,
17442113a128SIris Chen     .minimum_version_id = 1,
17452113a128SIris Chen     .needed = m25p80_block_protect_needed,
17462113a128SIris Chen     .fields = (VMStateField[]) {
17472113a128SIris Chen         VMSTATE_BOOL(block_protect0, Flash),
17482113a128SIris Chen         VMSTATE_BOOL(block_protect1, Flash),
17492113a128SIris Chen         VMSTATE_BOOL(block_protect2, Flash),
17502113a128SIris Chen         VMSTATE_BOOL(block_protect3, Flash),
17512113a128SIris Chen         VMSTATE_BOOL(top_bottom_bit, Flash),
17522113a128SIris Chen         VMSTATE_END_OF_LIST()
17532113a128SIris Chen     }
17542113a128SIris Chen };
17552113a128SIris Chen 
175649ab747fSPaolo Bonzini static const VMStateDescription vmstate_m25p80 = {
1757c827c06aSMarcin Krzeminski     .name = "m25p80",
1758c827c06aSMarcin Krzeminski     .version_id = 0,
1759c827c06aSMarcin Krzeminski     .minimum_version_id = 0,
176049ab747fSPaolo Bonzini     .pre_save = m25p80_pre_save,
17610add925fSFrancisco Iglesias     .pre_load = m25p80_pre_load,
176249ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
176349ab747fSPaolo Bonzini         VMSTATE_UINT8(state, Flash),
176424cb2e0dSJean-Christophe Dubois         VMSTATE_UINT8_ARRAY(data, Flash, M25P80_INTERNAL_DATA_BUFFER_SZ),
176549ab747fSPaolo Bonzini         VMSTATE_UINT32(len, Flash),
176649ab747fSPaolo Bonzini         VMSTATE_UINT32(pos, Flash),
176749ab747fSPaolo Bonzini         VMSTATE_UINT8(needed_bytes, Flash),
176849ab747fSPaolo Bonzini         VMSTATE_UINT8(cmd_in_progress, Flash),
1769b7f480c3SPaolo Bonzini         VMSTATE_UINT32(cur_addr, Flash),
177049ab747fSPaolo Bonzini         VMSTATE_BOOL(write_enable, Flash),
1771c827c06aSMarcin Krzeminski         VMSTATE_BOOL(reset_enable, Flash),
1772c827c06aSMarcin Krzeminski         VMSTATE_UINT8(ear, Flash),
1773c827c06aSMarcin Krzeminski         VMSTATE_BOOL(four_bytes_address_mode, Flash),
1774c827c06aSMarcin Krzeminski         VMSTATE_UINT32(nonvolatile_cfg, Flash),
1775c827c06aSMarcin Krzeminski         VMSTATE_UINT32(volatile_cfg, Flash),
1776c827c06aSMarcin Krzeminski         VMSTATE_UINT32(enh_volatile_cfg, Flash),
1777c827c06aSMarcin Krzeminski         VMSTATE_BOOL(quad_enable, Flash),
1778c827c06aSMarcin Krzeminski         VMSTATE_UINT8(spansion_cr1nv, Flash),
1779c827c06aSMarcin Krzeminski         VMSTATE_UINT8(spansion_cr2nv, Flash),
1780c827c06aSMarcin Krzeminski         VMSTATE_UINT8(spansion_cr3nv, Flash),
1781c827c06aSMarcin Krzeminski         VMSTATE_UINT8(spansion_cr4nv, Flash),
178249ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
17830add925fSFrancisco Iglesias     },
17840add925fSFrancisco Iglesias     .subsections = (const VMStateDescription * []) {
17850add925fSFrancisco Iglesias         &vmstate_m25p80_data_read_loop,
1786465ef47aSXuzhou Cheng         &vmstate_m25p80_aai_enable,
17872fa22a0fSIris Chen         &vmstate_m25p80_write_protect,
17882113a128SIris Chen         &vmstate_m25p80_block_protect,
17890add925fSFrancisco Iglesias         NULL
179049ab747fSPaolo Bonzini     }
179149ab747fSPaolo Bonzini };
179249ab747fSPaolo Bonzini 
m25p80_class_init(ObjectClass * klass,void * data)179349ab747fSPaolo Bonzini static void m25p80_class_init(ObjectClass *klass, void *data)
179449ab747fSPaolo Bonzini {
179549ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
1796ec7e429bSPhilippe Mathieu-Daudé     SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
179749ab747fSPaolo Bonzini     M25P80Class *mc = M25P80_CLASS(klass);
179849ab747fSPaolo Bonzini 
17997673bb4cSCédric Le Goater     k->realize = m25p80_realize;
180049ab747fSPaolo Bonzini     k->transfer = m25p80_transfer8;
180149ab747fSPaolo Bonzini     k->set_cs = m25p80_cs;
180249ab747fSPaolo Bonzini     k->cs_polarity = SSI_CS_LOW;
180349ab747fSPaolo Bonzini     dc->vmsd = &vmstate_m25p80;
18044f67d30bSMarc-André Lureau     device_class_set_props(dc, m25p80_properties);
1805187c2636SMarcin Krzeminski     dc->reset = m25p80_reset;
180649ab747fSPaolo Bonzini     mc->pi = data;
180749ab747fSPaolo Bonzini }
180849ab747fSPaolo Bonzini 
180949ab747fSPaolo Bonzini static const TypeInfo m25p80_info = {
181049ab747fSPaolo Bonzini     .name           = TYPE_M25P80,
1811ec7e429bSPhilippe Mathieu-Daudé     .parent         = TYPE_SSI_PERIPHERAL,
181249ab747fSPaolo Bonzini     .instance_size  = sizeof(Flash),
181349ab747fSPaolo Bonzini     .class_size     = sizeof(M25P80Class),
181449ab747fSPaolo Bonzini     .abstract       = true,
181549ab747fSPaolo Bonzini };
181649ab747fSPaolo Bonzini 
m25p80_register_types(void)181749ab747fSPaolo Bonzini static void m25p80_register_types(void)
181849ab747fSPaolo Bonzini {
181949ab747fSPaolo Bonzini     int i;
182049ab747fSPaolo Bonzini 
182149ab747fSPaolo Bonzini     type_register_static(&m25p80_info);
182249ab747fSPaolo Bonzini     for (i = 0; i < ARRAY_SIZE(known_devices); ++i) {
182349ab747fSPaolo Bonzini         TypeInfo ti = {
182449ab747fSPaolo Bonzini             .name       = known_devices[i].part_name,
182549ab747fSPaolo Bonzini             .parent     = TYPE_M25P80,
182649ab747fSPaolo Bonzini             .class_init = m25p80_class_init,
182749ab747fSPaolo Bonzini             .class_data = (void *)&known_devices[i],
182849ab747fSPaolo Bonzini         };
182949ab747fSPaolo Bonzini         type_register(&ti);
183049ab747fSPaolo Bonzini     }
183149ab747fSPaolo Bonzini }
183249ab747fSPaolo Bonzini 
type_init(m25p80_register_types)183349ab747fSPaolo Bonzini type_init(m25p80_register_types)
1834*71df78a3SCédric Le Goater 
1835*71df78a3SCédric Le Goater BlockBackend *m25p80_get_blk(DeviceState *dev)
1836*71df78a3SCédric Le Goater {
1837*71df78a3SCédric Le Goater     return M25P80(dev)->blk;
1838*71df78a3SCédric Le Goater }
1839