xref: /openbmc/qemu/hw/block/m25p80.c (revision cea8ac78545a83e1f01c94d89d6f5a3f6b5c05d2)
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"
289ab26b0eSCé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;
64c0400e3aSJamin Lin     /*
65c0400e3aSJamin Lin      * there is confusion between manufacturers as to what a sector is. In this
6649ab747fSPaolo Bonzini      * device model, a "sector" is the size that is erased by the ERASE_SECTOR
6749ab747fSPaolo Bonzini      * command (opcode 0xd8).
6849ab747fSPaolo Bonzini      */
6949ab747fSPaolo Bonzini     uint32_t sector_size;
7049ab747fSPaolo Bonzini     uint32_t n_sectors;
7149ab747fSPaolo Bonzini     uint32_t page_size;
7276e87269SMarcin Krzeminski     uint16_t flags;
73f509dfeeSMarcin Krzeminski     /*
74f509dfeeSMarcin Krzeminski      * Big sized spi nor are often stacked devices, thus sometime
75f509dfeeSMarcin Krzeminski      * replace chip erase with die erase.
76f509dfeeSMarcin Krzeminski      * This field inform how many die is in the chip.
77f509dfeeSMarcin Krzeminski      */
78f509dfeeSMarcin Krzeminski     uint8_t die_cnt;
792389bcc2SCédric Le Goater     uint8_t (*sfdp_read)(uint32_t sfdp_addr);
8049ab747fSPaolo Bonzini } FlashPartInfo;
8149ab747fSPaolo Bonzini 
8249ab747fSPaolo Bonzini /* adapted from linux */
83e3ba6cd6SMarcin Krzeminski /* Used when the "_ext_id" is two bytes at most */
84e3ba6cd6SMarcin Krzeminski #define INFO(_part_name, _jedec_id, _ext_id, _sector_size, _n_sectors, _flags)\
85e3ba6cd6SMarcin Krzeminski     .part_name = _part_name,\
86e3ba6cd6SMarcin Krzeminski     .id = {\
87e3ba6cd6SMarcin Krzeminski         ((_jedec_id) >> 16) & 0xff,\
88e3ba6cd6SMarcin Krzeminski         ((_jedec_id) >> 8) & 0xff,\
89e3ba6cd6SMarcin Krzeminski         (_jedec_id) & 0xff,\
90e3ba6cd6SMarcin Krzeminski         ((_ext_id) >> 8) & 0xff,\
91e3ba6cd6SMarcin Krzeminski         (_ext_id) & 0xff,\
92e3ba6cd6SMarcin Krzeminski           },\
93e3ba6cd6SMarcin Krzeminski     .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),\
94e3ba6cd6SMarcin Krzeminski     .sector_size = (_sector_size),\
95e3ba6cd6SMarcin Krzeminski     .n_sectors = (_n_sectors),\
96e3ba6cd6SMarcin Krzeminski     .page_size = 256,\
97f509dfeeSMarcin Krzeminski     .flags = (_flags),\
98f509dfeeSMarcin Krzeminski     .die_cnt = 0
9949ab747fSPaolo Bonzini 
100e3ba6cd6SMarcin Krzeminski #define INFO6(_part_name, _jedec_id, _ext_id, _sector_size, _n_sectors, _flags)\
101e3ba6cd6SMarcin Krzeminski     .part_name = _part_name,\
102e3ba6cd6SMarcin Krzeminski     .id = {\
103e3ba6cd6SMarcin Krzeminski         ((_jedec_id) >> 16) & 0xff,\
104e3ba6cd6SMarcin Krzeminski         ((_jedec_id) >> 8) & 0xff,\
105e3ba6cd6SMarcin Krzeminski         (_jedec_id) & 0xff,\
106e3ba6cd6SMarcin Krzeminski         ((_ext_id) >> 16) & 0xff,\
107e3ba6cd6SMarcin Krzeminski         ((_ext_id) >> 8) & 0xff,\
108e3ba6cd6SMarcin Krzeminski         (_ext_id) & 0xff,\
109e3ba6cd6SMarcin Krzeminski           },\
110e3ba6cd6SMarcin Krzeminski     .id_len = 6,\
11149ab747fSPaolo Bonzini     .sector_size = (_sector_size),\
11249ab747fSPaolo Bonzini     .n_sectors = (_n_sectors),\
11349ab747fSPaolo Bonzini     .page_size = 256,\
11449ab747fSPaolo Bonzini     .flags = (_flags),\
115f509dfeeSMarcin Krzeminski     .die_cnt = 0
116f509dfeeSMarcin Krzeminski 
117f509dfeeSMarcin Krzeminski #define INFO_STACKED(_part_name, _jedec_id, _ext_id, _sector_size, _n_sectors,\
118f509dfeeSMarcin Krzeminski                     _flags, _die_cnt)\
119f509dfeeSMarcin Krzeminski     .part_name = _part_name,\
120f509dfeeSMarcin Krzeminski     .id = {\
121f509dfeeSMarcin Krzeminski         ((_jedec_id) >> 16) & 0xff,\
122f509dfeeSMarcin Krzeminski         ((_jedec_id) >> 8) & 0xff,\
123f509dfeeSMarcin Krzeminski         (_jedec_id) & 0xff,\
124f509dfeeSMarcin Krzeminski         ((_ext_id) >> 8) & 0xff,\
125f509dfeeSMarcin Krzeminski         (_ext_id) & 0xff,\
126f509dfeeSMarcin Krzeminski           },\
127f509dfeeSMarcin Krzeminski     .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),\
128f509dfeeSMarcin Krzeminski     .sector_size = (_sector_size),\
129f509dfeeSMarcin Krzeminski     .n_sectors = (_n_sectors),\
130f509dfeeSMarcin Krzeminski     .page_size = 256,\
131f509dfeeSMarcin Krzeminski     .flags = (_flags),\
132f509dfeeSMarcin Krzeminski     .die_cnt = _die_cnt
13349ab747fSPaolo Bonzini 
13449ab747fSPaolo Bonzini #define JEDEC_NUMONYX 0x20
13549ab747fSPaolo Bonzini #define JEDEC_WINBOND 0xEF
13649ab747fSPaolo Bonzini #define JEDEC_SPANSION 0x01
13749ab747fSPaolo Bonzini 
138cb475951SMarcin Krzeminski /* Numonyx (Micron) Configuration register macros */
139cb475951SMarcin Krzeminski #define VCFG_DUMMY 0x1
140cb475951SMarcin Krzeminski #define VCFG_WRAP_SEQUENTIAL 0x2
141cb475951SMarcin Krzeminski #define NVCFG_XIP_MODE_DISABLED (7 << 9)
142cb475951SMarcin Krzeminski #define NVCFG_XIP_MODE_MASK (7 << 9)
14309414144SJoe Komlodi #define VCFG_XIP_MODE_DISABLED (1 << 3)
144cb475951SMarcin Krzeminski #define CFG_DUMMY_CLK_LEN 4
145cb475951SMarcin Krzeminski #define NVCFG_DUMMY_CLK_POS 12
146cb475951SMarcin Krzeminski #define VCFG_DUMMY_CLK_POS 4
1475c765e7aSStefan Weil #define EVCFG_OUT_DRIVER_STRENGTH_DEF 7
148cb475951SMarcin Krzeminski #define EVCFG_VPP_ACCELERATOR (1 << 3)
149cb475951SMarcin Krzeminski #define EVCFG_RESET_HOLD_ENABLED (1 << 4)
150cb475951SMarcin Krzeminski #define NVCFG_DUAL_IO_MASK (1 << 2)
15109414144SJoe Komlodi #define EVCFG_DUAL_IO_DISABLED (1 << 6)
152cb475951SMarcin Krzeminski #define NVCFG_QUAD_IO_MASK (1 << 3)
15309414144SJoe Komlodi #define EVCFG_QUAD_IO_DISABLED (1 << 7)
154cb475951SMarcin Krzeminski #define NVCFG_4BYTE_ADDR_MASK (1 << 0)
155cb475951SMarcin Krzeminski #define NVCFG_LOWER_SEGMENT_MASK (1 << 1)
156cb475951SMarcin Krzeminski 
1579fbaa364SMarcin Krzeminski /* Numonyx (Micron) Flag Status Register macros */
1589fbaa364SMarcin Krzeminski #define FSR_4BYTE_ADDR_MODE_ENABLED 0x1
1599fbaa364SMarcin Krzeminski #define FSR_FLASH_READY (1 << 7)
1609fbaa364SMarcin Krzeminski 
161d9cc8701SMarcin Krzeminski /* Spansion configuration registers macros. */
162d9cc8701SMarcin Krzeminski #define SPANSION_QUAD_CFG_POS 0
163d9cc8701SMarcin Krzeminski #define SPANSION_QUAD_CFG_LEN 1
164d9cc8701SMarcin Krzeminski #define SPANSION_DUMMY_CLK_POS 0
165d9cc8701SMarcin Krzeminski #define SPANSION_DUMMY_CLK_LEN 4
166d9cc8701SMarcin Krzeminski #define SPANSION_ADDR_LEN_POS 7
167d9cc8701SMarcin Krzeminski #define SPANSION_ADDR_LEN_LEN 1
168d9cc8701SMarcin Krzeminski 
169cf6f1efeSMarcin Krzeminski /*
170cf6f1efeSMarcin Krzeminski  * Spansion read mode command length in bytes,
171cf6f1efeSMarcin Krzeminski  * the mode is currently not supported.
172cf6f1efeSMarcin Krzeminski  */
173cf6f1efeSMarcin Krzeminski 
174cf6f1efeSMarcin Krzeminski #define SPANSION_CONTINUOUS_READ_MODE_CMD_LEN 1
175fe847705SMarcin Krzeminski #define WINBOND_CONTINUOUS_READ_MODE_CMD_LEN 1
176cf6f1efeSMarcin Krzeminski 
17749ab747fSPaolo Bonzini static const FlashPartInfo known_devices[] = {
17849ab747fSPaolo Bonzini     /* Atmel -- some are (confusingly) marketed as "DataFlash" */
17949ab747fSPaolo Bonzini     { INFO("at25fs010",   0x1f6601,      0,  32 << 10,   4, ER_4K) },
18049ab747fSPaolo Bonzini     { INFO("at25fs040",   0x1f6604,      0,  64 << 10,   8, ER_4K) },
18149ab747fSPaolo Bonzini 
18249ab747fSPaolo Bonzini     { INFO("at25df041a",  0x1f4401,      0,  64 << 10,   8, ER_4K) },
18349ab747fSPaolo Bonzini     { INFO("at25df321a",  0x1f4701,      0,  64 << 10,  64, ER_4K) },
18449ab747fSPaolo Bonzini     { INFO("at25df641",   0x1f4800,      0,  64 << 10, 128, ER_4K) },
18549ab747fSPaolo Bonzini 
18649ab747fSPaolo Bonzini     { INFO("at26f004",    0x1f0400,      0,  64 << 10,   8, ER_4K) },
18749ab747fSPaolo Bonzini     { INFO("at26df081a",  0x1f4501,      0,  64 << 10,  16, ER_4K) },
18849ab747fSPaolo Bonzini     { INFO("at26df161a",  0x1f4601,      0,  64 << 10,  32, ER_4K) },
18949ab747fSPaolo Bonzini     { INFO("at26df321",   0x1f4700,      0,  64 << 10,  64, ER_4K) },
19049ab747fSPaolo Bonzini 
1913e758c1dSEd Maste     { INFO("at45db081d",  0x1f2500,      0,  64 << 10,  16, ER_4K) },
1923e758c1dSEd Maste 
193c0400e3aSJamin Lin     /*
194c0400e3aSJamin Lin      * Atmel EEPROMS - it is assumed, that don't care bit in command
1951435bcd6SMarcin Krzeminski      * is set to 0. Block protection is not supported.
1961435bcd6SMarcin Krzeminski      */
1971435bcd6SMarcin Krzeminski     { INFO("at25128a-nonjedec", 0x0,     0,         1, 131072, EEPROM) },
1981435bcd6SMarcin Krzeminski     { INFO("at25256a-nonjedec", 0x0,     0,         1, 262144, EEPROM) },
1991435bcd6SMarcin Krzeminski 
20049ab747fSPaolo Bonzini     /* EON -- en25xxx */
20149ab747fSPaolo Bonzini     { INFO("en25f32",     0x1c3116,      0,  64 << 10,  64, ER_4K) },
20249ab747fSPaolo Bonzini     { INFO("en25p32",     0x1c2016,      0,  64 << 10,  64, 0) },
20349ab747fSPaolo Bonzini     { INFO("en25q32b",    0x1c3016,      0,  64 << 10,  64, 0) },
20449ab747fSPaolo Bonzini     { INFO("en25p64",     0x1c2017,      0,  64 << 10, 128, 0) },
2053e758c1dSEd Maste     { INFO("en25q64",     0x1c3017,      0,  64 << 10, 128, ER_4K) },
2063e758c1dSEd Maste 
2073e758c1dSEd Maste     /* GigaDevice */
2083e758c1dSEd Maste     { INFO("gd25q32",     0xc84016,      0,  64 << 10,  64, ER_4K) },
2093e758c1dSEd Maste     { INFO("gd25q64",     0xc84017,      0,  64 << 10, 128, ER_4K) },
21049ab747fSPaolo Bonzini 
21149ab747fSPaolo Bonzini     /* Intel/Numonyx -- xxxs33b */
21249ab747fSPaolo Bonzini     { INFO("160s33b",     0x898911,      0,  64 << 10,  32, 0) },
21349ab747fSPaolo Bonzini     { INFO("320s33b",     0x898912,      0,  64 << 10,  64, 0) },
21449ab747fSPaolo Bonzini     { INFO("640s33b",     0x898913,      0,  64 << 10, 128, 0) },
2153e758c1dSEd Maste     { INFO("n25q064",     0x20ba17,      0,  64 << 10, 128, 0) },
21649ab747fSPaolo Bonzini 
21762d10766SBin Meng     /* ISSI */
21862d10766SBin Meng     { INFO("is25lq040b",  0x9d4013,      0,  64 << 10,   8, ER_4K) },
21962d10766SBin Meng     { INFO("is25lp080d",  0x9d6014,      0,  64 << 10,  16, ER_4K) },
22062d10766SBin Meng     { INFO("is25lp016d",  0x9d6015,      0,  64 << 10,  32, ER_4K) },
22162d10766SBin Meng     { INFO("is25lp032",   0x9d6016,      0,  64 << 10,  64, ER_4K) },
22262d10766SBin Meng     { INFO("is25lp064",   0x9d6017,      0,  64 << 10, 128, ER_4K) },
22362d10766SBin Meng     { INFO("is25lp128",   0x9d6018,      0,  64 << 10, 256, ER_4K) },
22462d10766SBin Meng     { INFO("is25lp256",   0x9d6019,      0,  64 << 10, 512, ER_4K) },
22562d10766SBin Meng     { INFO("is25wp032",   0x9d7016,      0,  64 << 10,  64, ER_4K) },
22662d10766SBin Meng     { INFO("is25wp064",   0x9d7017,      0,  64 << 10, 128, ER_4K) },
22762d10766SBin Meng     { INFO("is25wp128",   0x9d7018,      0,  64 << 10, 256, ER_4K) },
2283e7808deSGuenter Roeck     { INFO("is25wp256",   0x9d7019,      0,  64 << 10, 512, ER_4K),
2293e7808deSGuenter Roeck       .sfdp_read = m25p80_sfdp_is25wp256 },
23062d10766SBin Meng 
23149ab747fSPaolo Bonzini     /* Macronix */
2323e758c1dSEd Maste     { INFO("mx25l2005a",  0xc22012,      0,  64 << 10,   4, ER_4K) },
23349ab747fSPaolo Bonzini     { INFO("mx25l4005a",  0xc22013,      0,  64 << 10,   8, ER_4K) },
23449ab747fSPaolo Bonzini     { INFO("mx25l8005",   0xc22014,      0,  64 << 10,  16, 0) },
23549ab747fSPaolo Bonzini     { INFO("mx25l1606e",  0xc22015,      0,  64 << 10,  32, ER_4K) },
23649ab747fSPaolo Bonzini     { INFO("mx25l3205d",  0xc22016,      0,  64 << 10,  64, 0) },
23749ab747fSPaolo Bonzini     { INFO("mx25l6405d",  0xc22017,      0,  64 << 10, 128, 0) },
23849ab747fSPaolo Bonzini     { INFO("mx25l12805d", 0xc22018,      0,  64 << 10, 256, 0) },
23949ab747fSPaolo Bonzini     { INFO("mx25l12855e", 0xc22618,      0,  64 << 10, 256, 0) },
2400c14a3c7SCédric Le Goater     { INFO6("mx25l25635e", 0xc22019,     0xc22019,  64 << 10, 512,
241dc907a66SCédric Le Goater             ER_4K | ER_32K), .sfdp_read = m25p80_sfdp_mx25l25635e },
24251f4613dSCédric Le Goater     { INFO6("mx25l25635f", 0xc22019,     0xc22019,  64 << 10, 512,
24351f4613dSCédric Le Goater             ER_4K | ER_32K), .sfdp_read = m25p80_sfdp_mx25l25635f },
24449ab747fSPaolo Bonzini     { INFO("mx25l25655e", 0xc22619,      0,  64 << 10, 512, 0) },
245ddd8ab19SIgor Kononenko     { INFO("mx66l51235f", 0xc2201a,      0,  64 << 10, 1024, ER_4K | ER_32K) },
246dadb2f90SMarcin Krzeminski     { INFO("mx66u51235f", 0xc2253a,      0,  64 << 10, 1024, ER_4K | ER_32K) },
247dadb2f90SMarcin Krzeminski     { INFO("mx66u1g45g",  0xc2253b,      0,  64 << 10, 2048, ER_4K | ER_32K) },
24852514908SCédric Le Goater     { INFO("mx66l1g45g",  0xc2201b,      0,  64 << 10, 2048, ER_4K | ER_32K),
24952514908SCédric Le Goater       .sfdp_read = m25p80_sfdp_mx66l1g45g },
25049ab747fSPaolo Bonzini 
2513e758c1dSEd Maste     /* Micron */
252f5aac8e0SEd Maste     { INFO("n25q032a11",  0x20bb16,      0,  64 << 10,  64, ER_4K) },
253f5aac8e0SEd Maste     { INFO("n25q032a13",  0x20ba16,      0,  64 << 10,  64, ER_4K) },
254f5aac8e0SEd Maste     { INFO("n25q064a11",  0x20bb17,      0,  64 << 10, 128, ER_4K) },
255f5aac8e0SEd Maste     { INFO("n25q064a13",  0x20ba17,      0,  64 << 10, 128, ER_4K) },
256f5aac8e0SEd Maste     { INFO("n25q128a11",  0x20bb18,      0,  64 << 10, 256, ER_4K) },
257f5aac8e0SEd Maste     { INFO("n25q128a13",  0x20ba18,      0,  64 << 10, 256, ER_4K) },
258f5aac8e0SEd Maste     { INFO("n25q256a11",  0x20bb19,      0,  64 << 10, 512, ER_4K) },
2595eb24fbdSCédric Le Goater     { INFO("n25q256a13",  0x20ba19,      0,  64 << 10, 512, ER_4K),
2605eb24fbdSCédric Le Goater       .sfdp_read = m25p80_sfdp_n25q256a },
26153dc9c79SFrancisco Iglesias     { INFO("n25q512a11",  0x20bb20,      0,  64 << 10, 1024, ER_4K) },
26253dc9c79SFrancisco Iglesias     { INFO("n25q512a13",  0x20ba20,      0,  64 << 10, 1024, ER_4K) },
263dadb2f90SMarcin Krzeminski     { INFO("n25q128",     0x20ba18,      0,  64 << 10, 256, 0) },
2642113a128SIris Chen     { INFO("n25q256a",    0x20ba19,      0,  64 << 10, 512,
2655eb24fbdSCédric Le Goater            ER_4K | HAS_SR_BP3_BIT6 | HAS_SR_TB),
2665eb24fbdSCédric Le Goater       .sfdp_read = m25p80_sfdp_n25q256a },
267dadb2f90SMarcin Krzeminski    { INFO("n25q512a",    0x20ba20,      0,  64 << 10, 1024, ER_4K) },
26831fc566fSCédric Le Goater     { INFO("n25q512ax3",  0x20ba20,  0x1000,  64 << 10, 1024, ER_4K) },
269ddd8ab19SIgor Kononenko     { INFO("mt25ql512ab", 0x20ba20, 0x1044, 64 << 10, 1024, ER_4K | ER_32K) },
2706b3fac72SFrancisco Iglesias     { INFO_STACKED("mt35xu01g", 0x2c5b1b, 0x104100, 128 << 10, 1024,
2711efbcf0bSShiva sagar Myana                    ER_4K | ER_32K, 2),
2721efbcf0bSShiva sagar Myana                    .sfdp_read = m25p80_sfdp_mt35xu01g },
2734d85bfc8SSai Pavan Boddu     { INFO_STACKED("mt35xu02gbba", 0x2c5b1c, 0x104100, 128 << 10, 2048,
2744d85bfc8SSai Pavan Boddu                    ER_4K | ER_32K, 4),
2754d85bfc8SSai Pavan Boddu                    .sfdp_read = m25p80_sfdp_mt35xu02g },
276eca27213SMarcin Krzeminski     { INFO_STACKED("n25q00",    0x20ba21, 0x1000, 64 << 10, 2048, ER_4K, 4) },
277eca27213SMarcin Krzeminski     { INFO_STACKED("n25q00a",   0x20bb21, 0x1000, 64 << 10, 2048, ER_4K, 4) },
278eca27213SMarcin Krzeminski     { INFO_STACKED("mt25ql01g", 0x20ba21, 0x1040, 64 << 10, 2048, ER_4K, 2) },
279eca27213SMarcin Krzeminski     { INFO_STACKED("mt25qu01g", 0x20bb21, 0x1040, 64 << 10, 2048, ER_4K, 2) },
280c0400e3aSJamin Lin     { INFO_STACKED("mt25ql02g", 0x20ba22, 0x1040, 64 << 10, 4096,
281c0400e3aSJamin Lin                    ER_4K | ER_32K, 2) },
282c0400e3aSJamin Lin     { INFO_STACKED("mt25qu02g", 0x20bb22, 0x1040, 64 << 10, 4096,
283c0400e3aSJamin Lin                    ER_4K | ER_32K, 2) },
2843e758c1dSEd Maste 
285c0400e3aSJamin Lin     /*
286c0400e3aSJamin Lin      * Spansion -- single (large) sector size only, at least
28749ab747fSPaolo Bonzini      * for the chips listed here (without boot sectors).
28849ab747fSPaolo Bonzini      */
28949ab747fSPaolo Bonzini     { INFO("s25sl032p",   0x010215, 0x4d00,  64 << 10,  64, ER_4K) },
2903e758c1dSEd Maste     { INFO("s25sl064p",   0x010216, 0x4d00,  64 << 10, 128, ER_4K) },
29149ab747fSPaolo Bonzini     { INFO("s25fl256s0",  0x010219, 0x4d00, 256 << 10, 128, 0) },
29249ab747fSPaolo Bonzini     { INFO("s25fl256s1",  0x010219, 0x4d01,  64 << 10, 512, 0) },
293dadb2f90SMarcin Krzeminski     { INFO6("s25fl512s",  0x010220, 0x4d0080, 256 << 10, 256, 0) },
294dadb2f90SMarcin Krzeminski     { INFO6("s70fl01gs",  0x010221, 0x4d0080, 256 << 10, 512, 0) },
29549ab747fSPaolo Bonzini     { INFO("s25sl12800",  0x012018, 0x0300, 256 << 10,  64, 0) },
29649ab747fSPaolo Bonzini     { INFO("s25sl12801",  0x012018, 0x0301,  64 << 10, 256, 0) },
29749ab747fSPaolo Bonzini     { INFO("s25fl129p0",  0x012018, 0x4d00, 256 << 10,  64, 0) },
29849ab747fSPaolo Bonzini     { INFO("s25fl129p1",  0x012018, 0x4d01,  64 << 10, 256, 0) },
2993e758c1dSEd Maste     { INFO("s25sl004a",   0x010212,      0,  64 << 10,   8, 0) },
3003e758c1dSEd Maste     { INFO("s25sl008a",   0x010213,      0,  64 << 10,  16, 0) },
3013e758c1dSEd Maste     { INFO("s25sl016a",   0x010214,      0,  64 << 10,  32, 0) },
3023e758c1dSEd Maste     { INFO("s25sl032a",   0x010215,      0,  64 << 10,  64, 0) },
3033e758c1dSEd Maste     { INFO("s25sl064a",   0x010216,      0,  64 << 10, 128, 0) },
30449ab747fSPaolo Bonzini     { INFO("s25fl016k",   0xef4015,      0,  64 << 10,  32, ER_4K | ER_32K) },
30549ab747fSPaolo Bonzini     { INFO("s25fl064k",   0xef4017,      0,  64 << 10, 128, ER_4K | ER_32K) },
30649ab747fSPaolo Bonzini 
307dadb2f90SMarcin Krzeminski     /* Spansion --  boot sectors support  */
308dadb2f90SMarcin Krzeminski     { INFO6("s25fs512s",    0x010220, 0x4d0081, 256 << 10, 256, 0) },
309dadb2f90SMarcin Krzeminski     { INFO6("s70fs01gs",    0x010221, 0x4d0081, 256 << 10, 512, 0) },
310dadb2f90SMarcin Krzeminski 
31149ab747fSPaolo Bonzini     /* SST -- large erase sizes are "overlays", "sectors" are 4<< 10 */
31249ab747fSPaolo Bonzini     { INFO("sst25vf040b", 0xbf258d,      0,  64 << 10,   8, ER_4K) },
31349ab747fSPaolo Bonzini     { INFO("sst25vf080b", 0xbf258e,      0,  64 << 10,  16, ER_4K) },
31449ab747fSPaolo Bonzini     { INFO("sst25vf016b", 0xbf2541,      0,  64 << 10,  32, ER_4K) },
31549ab747fSPaolo Bonzini     { INFO("sst25vf032b", 0xbf254a,      0,  64 << 10,  64, ER_4K) },
31649ab747fSPaolo Bonzini     { INFO("sst25wf512",  0xbf2501,      0,  64 << 10,   1, ER_4K) },
31749ab747fSPaolo Bonzini     { INFO("sst25wf010",  0xbf2502,      0,  64 << 10,   2, ER_4K) },
31849ab747fSPaolo Bonzini     { INFO("sst25wf020",  0xbf2503,      0,  64 << 10,   4, ER_4K) },
31949ab747fSPaolo Bonzini     { INFO("sst25wf040",  0xbf2504,      0,  64 << 10,   8, ER_4K) },
320d857c4c0SAlistair Francis     { INFO("sst25wf080",  0xbf2505,      0,  64 << 10,  16, ER_4K) },
32149ab747fSPaolo Bonzini 
32249ab747fSPaolo Bonzini     /* ST Microelectronics -- newer production may have feature updates */
32349ab747fSPaolo Bonzini     { INFO("m25p05",      0x202010,      0,  32 << 10,   2, 0) },
32449ab747fSPaolo Bonzini     { INFO("m25p10",      0x202011,      0,  32 << 10,   4, 0) },
32549ab747fSPaolo Bonzini     { INFO("m25p20",      0x202012,      0,  64 << 10,   4, 0) },
32649ab747fSPaolo Bonzini     { INFO("m25p40",      0x202013,      0,  64 << 10,   8, 0) },
32749ab747fSPaolo Bonzini     { INFO("m25p80",      0x202014,      0,  64 << 10,  16, 0) },
32849ab747fSPaolo Bonzini     { INFO("m25p16",      0x202015,      0,  64 << 10,  32, 0) },
32949ab747fSPaolo Bonzini     { INFO("m25p32",      0x202016,      0,  64 << 10,  64, 0) },
33049ab747fSPaolo Bonzini     { INFO("m25p64",      0x202017,      0,  64 << 10, 128, 0) },
33149ab747fSPaolo Bonzini     { INFO("m25p128",     0x202018,      0, 256 << 10,  64, 0) },
3323e758c1dSEd Maste     { INFO("n25q032",     0x20ba16,      0,  64 << 10,  64, 0) },
33349ab747fSPaolo Bonzini 
33449ab747fSPaolo Bonzini     { INFO("m45pe10",     0x204011,      0,  64 << 10,   2, 0) },
33549ab747fSPaolo Bonzini     { INFO("m45pe80",     0x204014,      0,  64 << 10,  16, 0) },
33649ab747fSPaolo Bonzini     { INFO("m45pe16",     0x204015,      0,  64 << 10,  32, 0) },
33749ab747fSPaolo Bonzini 
3383e758c1dSEd Maste     { INFO("m25pe20",     0x208012,      0,  64 << 10,   4, 0) },
33949ab747fSPaolo Bonzini     { INFO("m25pe80",     0x208014,      0,  64 << 10,  16, 0) },
34049ab747fSPaolo Bonzini     { INFO("m25pe16",     0x208015,      0,  64 << 10,  32, ER_4K) },
34149ab747fSPaolo Bonzini 
34249ab747fSPaolo Bonzini     { INFO("m25px32",     0x207116,      0,  64 << 10,  64, ER_4K) },
34349ab747fSPaolo Bonzini     { INFO("m25px32-s0",  0x207316,      0,  64 << 10,  64, ER_4K) },
34449ab747fSPaolo Bonzini     { INFO("m25px32-s1",  0x206316,      0,  64 << 10,  64, ER_4K) },
34549ab747fSPaolo Bonzini     { INFO("m25px64",     0x207117,      0,  64 << 10, 128, 0) },
34649ab747fSPaolo Bonzini 
34749ab747fSPaolo Bonzini     /* Winbond -- w25x "blocks" are 64k, "sectors" are 4KiB */
34849ab747fSPaolo Bonzini     { INFO("w25x10",      0xef3011,      0,  64 << 10,   2, ER_4K) },
34949ab747fSPaolo Bonzini     { INFO("w25x20",      0xef3012,      0,  64 << 10,   4, ER_4K) },
35049ab747fSPaolo Bonzini     { INFO("w25x40",      0xef3013,      0,  64 << 10,   8, ER_4K) },
35149ab747fSPaolo Bonzini     { INFO("w25x80",      0xef3014,      0,  64 << 10,  16, ER_4K) },
35249ab747fSPaolo Bonzini     { INFO("w25x16",      0xef3015,      0,  64 << 10,  32, ER_4K) },
35349ab747fSPaolo Bonzini     { INFO("w25x32",      0xef3016,      0,  64 << 10,  64, ER_4K) },
35449ab747fSPaolo Bonzini     { INFO("w25q32",      0xef4016,      0,  64 << 10,  64, ER_4K) },
3553e758c1dSEd Maste     { INFO("w25q32dw",    0xef6016,      0,  64 << 10,  64, ER_4K) },
35649ab747fSPaolo Bonzini     { INFO("w25x64",      0xef3017,      0,  64 << 10, 128, ER_4K) },
35749ab747fSPaolo Bonzini     { INFO("w25q64",      0xef4017,      0,  64 << 10, 128, ER_4K) },
3583e758c1dSEd Maste     { INFO("w25q80",      0xef5014,      0,  64 << 10,  16, ER_4K) },
359*146f078aSJamin Lin     { INFO("w25q80bl",    0xef4014,      0,  64 << 10,  16, ER_4K),
360*146f078aSJamin Lin       .sfdp_read = m25p80_sfdp_w25q80bl },
361e9041884SCédric Le Goater     { INFO("w25q256",     0xef4019,      0,  64 << 10, 512, ER_4K),
362e9041884SCédric Le Goater       .sfdp_read = m25p80_sfdp_w25q256 },
3638e57da58SCédric Le Goater     { INFO("w25q512jv",   0xef4020,      0,  64 << 10, 1024, ER_4K),
3648e57da58SCédric Le Goater       .sfdp_read = m25p80_sfdp_w25q512jv },
365a34b0d53SPatrick Williams     { INFO("w25q01jvq",   0xef4021,      0,  64 << 10, 2048, ER_4K),
366a34b0d53SPatrick Williams       .sfdp_read = m25p80_sfdp_w25q01jvq },
3678d970f41SChalapathi V 
3688d970f41SChalapathi V     /* Microchip */
3698d970f41SChalapathi V     { INFO("25csm04",      0x29cc00,      0x100,  64 << 10,  8, 0) },
37049ab747fSPaolo Bonzini };
37149ab747fSPaolo Bonzini 
37249ab747fSPaolo Bonzini typedef enum {
37349ab747fSPaolo Bonzini     NOP = 0,
37449ab747fSPaolo Bonzini     WRSR = 0x1,
37549ab747fSPaolo Bonzini     WRDI = 0x4,
37649ab747fSPaolo Bonzini     RDSR = 0x5,
37749ab747fSPaolo Bonzini     WREN = 0x6,
3780f589782SFrancisco Iglesias     BRRD = 0x16,
3790f589782SFrancisco Iglesias     BRWR = 0x17,
38049ab747fSPaolo Bonzini     JEDEC_READ = 0x9f,
3810f589782SFrancisco Iglesias     BULK_ERASE_60 = 0x60,
38249ab747fSPaolo Bonzini     BULK_ERASE = 0xc7,
3839fbaa364SMarcin Krzeminski     READ_FSR = 0x70,
3847a69c100SMarcin Krzeminski     RDCR = 0x15,
3852389bcc2SCédric Le Goater     RDSFDP = 0x5a,
38649ab747fSPaolo Bonzini 
38763e47f6fSMarcin Krzeminski     READ = 0x03,
38863e47f6fSMarcin Krzeminski     READ4 = 0x13,
38963e47f6fSMarcin Krzeminski     FAST_READ = 0x0b,
39063e47f6fSMarcin Krzeminski     FAST_READ4 = 0x0c,
39149ab747fSPaolo Bonzini     DOR = 0x3b,
39263e47f6fSMarcin Krzeminski     DOR4 = 0x3c,
39349ab747fSPaolo Bonzini     QOR = 0x6b,
39463e47f6fSMarcin Krzeminski     QOR4 = 0x6c,
39549ab747fSPaolo Bonzini     DIOR = 0xbb,
39663e47f6fSMarcin Krzeminski     DIOR4 = 0xbc,
39749ab747fSPaolo Bonzini     QIOR = 0xeb,
39863e47f6fSMarcin Krzeminski     QIOR4 = 0xec,
39949ab747fSPaolo Bonzini 
40063e47f6fSMarcin Krzeminski     PP = 0x02,
40163e47f6fSMarcin Krzeminski     PP4 = 0x12,
40230467afeSMarcin Krzeminski     PP4_4 = 0x3e,
40349ab747fSPaolo Bonzini     DPP = 0xa2,
40449ab747fSPaolo Bonzini     QPP = 0x32,
405597c15f0SMarcin Krzeminski     QPP_4 = 0x34,
406a87fc364SFrancisco Iglesias     RDID_90 = 0x90,
407a87fc364SFrancisco Iglesias     RDID_AB = 0xab,
408465ef47aSXuzhou Cheng     AAI_WP = 0xad,
40949ab747fSPaolo Bonzini 
41049ab747fSPaolo Bonzini     ERASE_4K = 0x20,
41163e47f6fSMarcin Krzeminski     ERASE4_4K = 0x21,
41249ab747fSPaolo Bonzini     ERASE_32K = 0x52,
41330467afeSMarcin Krzeminski     ERASE4_32K = 0x5c,
41449ab747fSPaolo Bonzini     ERASE_SECTOR = 0xd8,
41563e47f6fSMarcin Krzeminski     ERASE4_SECTOR = 0xdc,
416187c2636SMarcin Krzeminski 
417c0f3f675SMarcin Krzeminski     EN_4BYTE_ADDR = 0xB7,
418c0f3f675SMarcin Krzeminski     EX_4BYTE_ADDR = 0xE9,
419c0f3f675SMarcin Krzeminski 
420d8a29a7aSMarcin Krzeminski     EXTEND_ADDR_READ = 0xC8,
421d8a29a7aSMarcin Krzeminski     EXTEND_ADDR_WRITE = 0xC5,
422d8a29a7aSMarcin Krzeminski 
423187c2636SMarcin Krzeminski     RESET_ENABLE = 0x66,
424187c2636SMarcin Krzeminski     RESET_MEMORY = 0x99,
425cb475951SMarcin Krzeminski 
4267a69c100SMarcin Krzeminski     /*
4277a69c100SMarcin Krzeminski      * Micron: 0x35 - enable QPI
4287a69c100SMarcin Krzeminski      * Spansion: 0x35 - read control register
42961f93767SJamin Lin      * Winbond: 0x35 - quad enable
4307a69c100SMarcin Krzeminski      */
4317a69c100SMarcin Krzeminski     RDCR_EQIO = 0x35,
4327a69c100SMarcin Krzeminski     RSTQIO = 0xf5,
4337a69c100SMarcin Krzeminski 
4349785731eSJamin Lin     /*
4359785731eSJamin Lin      * Winbond: 0x31 - write status register 2
4369785731eSJamin Lin      */
4379785731eSJamin Lin     WRSR2 = 0x31,
4389785731eSJamin Lin 
439cb475951SMarcin Krzeminski     RNVCR = 0xB5,
440cb475951SMarcin Krzeminski     WNVCR = 0xB1,
441cb475951SMarcin Krzeminski 
442cb475951SMarcin Krzeminski     RVCR = 0x85,
443cb475951SMarcin Krzeminski     WVCR = 0x81,
444cb475951SMarcin Krzeminski 
445cb475951SMarcin Krzeminski     REVCR = 0x65,
446cb475951SMarcin Krzeminski     WEVCR = 0x61,
447f509dfeeSMarcin Krzeminski 
448f509dfeeSMarcin Krzeminski     DIE_ERASE = 0xC4,
44949ab747fSPaolo Bonzini } FlashCMD;
45049ab747fSPaolo Bonzini 
45149ab747fSPaolo Bonzini typedef enum {
45249ab747fSPaolo Bonzini     STATE_IDLE,
45349ab747fSPaolo Bonzini     STATE_PAGE_PROGRAM,
45449ab747fSPaolo Bonzini     STATE_READ,
45549ab747fSPaolo Bonzini     STATE_COLLECTING_DATA,
4569964674eSMarcin Krzeminski     STATE_COLLECTING_VAR_LEN_DATA,
45749ab747fSPaolo Bonzini     STATE_READING_DATA,
4582389bcc2SCédric Le Goater     STATE_READING_SFDP,
45949ab747fSPaolo Bonzini } CMDState;
46049ab747fSPaolo Bonzini 
461c7cd0a6cSMarcin Krzeminski typedef enum {
462c7cd0a6cSMarcin Krzeminski     MAN_SPANSION,
463c7cd0a6cSMarcin Krzeminski     MAN_MACRONIX,
464c7cd0a6cSMarcin Krzeminski     MAN_NUMONYX,
465c7cd0a6cSMarcin Krzeminski     MAN_WINBOND,
466a87fc364SFrancisco Iglesias     MAN_SST,
46710509e10SBin Meng     MAN_ISSI,
468c7cd0a6cSMarcin Krzeminski     MAN_GENERIC,
469c7cd0a6cSMarcin Krzeminski } Manufacturer;
470c7cd0a6cSMarcin Krzeminski 
47123486231SJoe Komlodi typedef enum {
47223486231SJoe Komlodi     MODE_STD = 0,
47323486231SJoe Komlodi     MODE_DIO = 1,
47423486231SJoe Komlodi     MODE_QIO = 2
47523486231SJoe Komlodi } SPIMode;
47623486231SJoe Komlodi 
47724cb2e0dSJean-Christophe Dubois #define M25P80_INTERNAL_DATA_BUFFER_SZ 16
47824cb2e0dSJean-Christophe Dubois 
479db1015e9SEduardo Habkost struct Flash {
480ec7e429bSPhilippe Mathieu-Daudé     SSIPeripheral parent_obj;
481cdccf7d7SPeter Crosthwaite 
4824be74634SMarkus Armbruster     BlockBackend *blk;
48349ab747fSPaolo Bonzini 
48449ab747fSPaolo Bonzini     uint8_t *storage;
48549ab747fSPaolo Bonzini     uint32_t size;
48649ab747fSPaolo Bonzini     int page_size;
48749ab747fSPaolo Bonzini 
48849ab747fSPaolo Bonzini     uint8_t state;
48924cb2e0dSJean-Christophe Dubois     uint8_t data[M25P80_INTERNAL_DATA_BUFFER_SZ];
49049ab747fSPaolo Bonzini     uint32_t len;
49149ab747fSPaolo Bonzini     uint32_t pos;
4920add925fSFrancisco Iglesias     bool data_read_loop;
49349ab747fSPaolo Bonzini     uint8_t needed_bytes;
49449ab747fSPaolo Bonzini     uint8_t cmd_in_progress;
495b7f480c3SPaolo Bonzini     uint32_t cur_addr;
496cb475951SMarcin Krzeminski     uint32_t nonvolatile_cfg;
497d9cc8701SMarcin Krzeminski     /* Configuration register for Macronix */
498cb475951SMarcin Krzeminski     uint32_t volatile_cfg;
499cb475951SMarcin Krzeminski     uint32_t enh_volatile_cfg;
500d9cc8701SMarcin Krzeminski     /* Spansion cfg registers. */
501d9cc8701SMarcin Krzeminski     uint8_t spansion_cr1nv;
502d9cc8701SMarcin Krzeminski     uint8_t spansion_cr2nv;
503d9cc8701SMarcin Krzeminski     uint8_t spansion_cr3nv;
504d9cc8701SMarcin Krzeminski     uint8_t spansion_cr4nv;
505d9cc8701SMarcin Krzeminski     uint8_t spansion_cr1v;
506d9cc8701SMarcin Krzeminski     uint8_t spansion_cr2v;
507d9cc8701SMarcin Krzeminski     uint8_t spansion_cr3v;
508d9cc8701SMarcin Krzeminski     uint8_t spansion_cr4v;
5092fa22a0fSIris Chen     bool wp_level;
51049ab747fSPaolo Bonzini     bool write_enable;
511c0f3f675SMarcin Krzeminski     bool four_bytes_address_mode;
512187c2636SMarcin Krzeminski     bool reset_enable;
5137a69c100SMarcin Krzeminski     bool quad_enable;
514465ef47aSXuzhou Cheng     bool aai_enable;
5152113a128SIris Chen     bool block_protect0;
5162113a128SIris Chen     bool block_protect1;
5172113a128SIris Chen     bool block_protect2;
5182113a128SIris Chen     bool block_protect3;
5192113a128SIris Chen     bool top_bottom_bit;
5202fa22a0fSIris Chen     bool status_register_write_disabled;
521d8a29a7aSMarcin Krzeminski     uint8_t ear;
52249ab747fSPaolo Bonzini 
52349ab747fSPaolo Bonzini     int64_t dirty_page;
52449ab747fSPaolo Bonzini 
52549ab747fSPaolo Bonzini     const FlashPartInfo *pi;
52649ab747fSPaolo Bonzini 
527db1015e9SEduardo Habkost };
52849ab747fSPaolo Bonzini 
529db1015e9SEduardo Habkost struct M25P80Class {
530ec7e429bSPhilippe Mathieu-Daudé     SSIPeripheralClass parent_class;
53149ab747fSPaolo Bonzini     FlashPartInfo *pi;
532db1015e9SEduardo Habkost };
53349ab747fSPaolo Bonzini 
OBJECT_DECLARE_TYPE(Flash,M25P80Class,M25P80)534a489d195SEduardo Habkost OBJECT_DECLARE_TYPE(Flash, M25P80Class, M25P80)
53549ab747fSPaolo Bonzini 
536c7cd0a6cSMarcin Krzeminski static inline Manufacturer get_man(Flash *s)
537c7cd0a6cSMarcin Krzeminski {
538e3ba6cd6SMarcin Krzeminski     switch (s->pi->id[0]) {
539c7cd0a6cSMarcin Krzeminski     case 0x20:
540c7cd0a6cSMarcin Krzeminski         return MAN_NUMONYX;
541c7cd0a6cSMarcin Krzeminski     case 0xEF:
542c7cd0a6cSMarcin Krzeminski         return MAN_WINBOND;
543c7cd0a6cSMarcin Krzeminski     case 0x01:
544c7cd0a6cSMarcin Krzeminski         return MAN_SPANSION;
545c7cd0a6cSMarcin Krzeminski     case 0xC2:
546c7cd0a6cSMarcin Krzeminski         return MAN_MACRONIX;
547a87fc364SFrancisco Iglesias     case 0xBF:
548a87fc364SFrancisco Iglesias         return MAN_SST;
54910509e10SBin Meng     case 0x9D:
55010509e10SBin Meng         return MAN_ISSI;
551c7cd0a6cSMarcin Krzeminski     default:
552c7cd0a6cSMarcin Krzeminski         return MAN_GENERIC;
553c7cd0a6cSMarcin Krzeminski     }
554c7cd0a6cSMarcin Krzeminski }
555c7cd0a6cSMarcin Krzeminski 
blk_sync_complete(void * opaque,int ret)5564be74634SMarkus Armbruster static void blk_sync_complete(void *opaque, int ret)
55749ab747fSPaolo Bonzini {
558cace7b80SPaolo Bonzini     QEMUIOVector *iov = opaque;
559cace7b80SPaolo Bonzini 
560cace7b80SPaolo Bonzini     qemu_iovec_destroy(iov);
561cace7b80SPaolo Bonzini     g_free(iov);
562cace7b80SPaolo Bonzini 
563c0400e3aSJamin Lin     /*
564c0400e3aSJamin Lin      * do nothing. Masters do not directly interact with the backing store,
56549ab747fSPaolo Bonzini      * only the working copy so no mutexing required.
56649ab747fSPaolo Bonzini      */
56749ab747fSPaolo Bonzini }
56849ab747fSPaolo Bonzini 
flash_sync_page(Flash * s,int page)56949ab747fSPaolo Bonzini static void flash_sync_page(Flash *s, int page)
57049ab747fSPaolo Bonzini {
571eef9f19eSShannon Zhao     QEMUIOVector *iov;
57249ab747fSPaolo Bonzini 
57386b1cf32SKevin Wolf     if (!s->blk || !blk_is_writable(s->blk)) {
574fc1084aaSPeter Crosthwaite         return;
575fc1084aaSPeter Crosthwaite     }
576fc1084aaSPeter Crosthwaite 
577eef9f19eSShannon Zhao     iov = g_new(QEMUIOVector, 1);
578cace7b80SPaolo Bonzini     qemu_iovec_init(iov, 1);
579cace7b80SPaolo Bonzini     qemu_iovec_add(iov, s->storage + page * s->pi->page_size,
580243e6f69SEric Blake                    s->pi->page_size);
581cace7b80SPaolo Bonzini     blk_aio_pwritev(s->blk, page * s->pi->page_size, iov, 0,
582cace7b80SPaolo Bonzini                     blk_sync_complete, iov);
58349ab747fSPaolo Bonzini }
58449ab747fSPaolo Bonzini 
flash_sync_area(Flash * s,int64_t off,int64_t len)58549ab747fSPaolo Bonzini static inline void flash_sync_area(Flash *s, int64_t off, int64_t len)
58649ab747fSPaolo Bonzini {
587eef9f19eSShannon Zhao     QEMUIOVector *iov;
58849ab747fSPaolo Bonzini 
58986b1cf32SKevin Wolf     if (!s->blk || !blk_is_writable(s->blk)) {
59049ab747fSPaolo Bonzini         return;
59149ab747fSPaolo Bonzini     }
59249ab747fSPaolo Bonzini 
59349ab747fSPaolo Bonzini     assert(!(len % BDRV_SECTOR_SIZE));
594eef9f19eSShannon Zhao     iov = g_new(QEMUIOVector, 1);
595cace7b80SPaolo Bonzini     qemu_iovec_init(iov, 1);
596cace7b80SPaolo Bonzini     qemu_iovec_add(iov, s->storage + off, len);
597cace7b80SPaolo Bonzini     blk_aio_pwritev(s->blk, off, iov, 0, blk_sync_complete, iov);
59849ab747fSPaolo Bonzini }
59949ab747fSPaolo Bonzini 
flash_erase(Flash * s,int offset,FlashCMD cmd)60049ab747fSPaolo Bonzini static void flash_erase(Flash *s, int offset, FlashCMD cmd)
60149ab747fSPaolo Bonzini {
60249ab747fSPaolo Bonzini     uint32_t len;
60349ab747fSPaolo Bonzini     uint8_t capa_to_assert = 0;
60449ab747fSPaolo Bonzini 
60549ab747fSPaolo Bonzini     switch (cmd) {
60649ab747fSPaolo Bonzini     case ERASE_4K:
60763e47f6fSMarcin Krzeminski     case ERASE4_4K:
608e8400cf3SPhilippe Mathieu-Daudé         len = 4 * KiB;
60949ab747fSPaolo Bonzini         capa_to_assert = ER_4K;
61049ab747fSPaolo Bonzini         break;
61149ab747fSPaolo Bonzini     case ERASE_32K:
61230467afeSMarcin Krzeminski     case ERASE4_32K:
613e8400cf3SPhilippe Mathieu-Daudé         len = 32 * KiB;
61449ab747fSPaolo Bonzini         capa_to_assert = ER_32K;
61549ab747fSPaolo Bonzini         break;
61649ab747fSPaolo Bonzini     case ERASE_SECTOR:
61763e47f6fSMarcin Krzeminski     case ERASE4_SECTOR:
61849ab747fSPaolo Bonzini         len = s->pi->sector_size;
61949ab747fSPaolo Bonzini         break;
62049ab747fSPaolo Bonzini     case BULK_ERASE:
62149ab747fSPaolo Bonzini         len = s->size;
62249ab747fSPaolo Bonzini         break;
623f509dfeeSMarcin Krzeminski     case DIE_ERASE:
624f509dfeeSMarcin Krzeminski         if (s->pi->die_cnt) {
625f509dfeeSMarcin Krzeminski             len = s->size / s->pi->die_cnt;
626f509dfeeSMarcin Krzeminski             offset = offset & (~(len - 1));
627f509dfeeSMarcin Krzeminski         } else {
628f509dfeeSMarcin Krzeminski             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: die erase is not supported"
629f509dfeeSMarcin Krzeminski                           " by device\n");
630f509dfeeSMarcin Krzeminski             return;
631f509dfeeSMarcin Krzeminski         }
632f509dfeeSMarcin Krzeminski         break;
63349ab747fSPaolo Bonzini     default:
63449ab747fSPaolo Bonzini         abort();
63549ab747fSPaolo Bonzini     }
63649ab747fSPaolo Bonzini 
637ccc46090SGuenter Roeck     trace_m25p80_flash_erase(s, offset, len);
638ccc46090SGuenter Roeck 
63949ab747fSPaolo Bonzini     if ((s->pi->flags & capa_to_assert) != capa_to_assert) {
640e9711b4dSPeter Crosthwaite         qemu_log_mask(LOG_GUEST_ERROR, "M25P80: %d erase size not supported by"
641e9711b4dSPeter Crosthwaite                       " device\n", len);
64249ab747fSPaolo Bonzini     }
64349ab747fSPaolo Bonzini 
64449ab747fSPaolo Bonzini     if (!s->write_enable) {
645e9711b4dSPeter Crosthwaite         qemu_log_mask(LOG_GUEST_ERROR, "M25P80: erase with write protect!\n");
64649ab747fSPaolo Bonzini         return;
64749ab747fSPaolo Bonzini     }
64849ab747fSPaolo Bonzini     memset(s->storage + offset, 0xff, len);
64949ab747fSPaolo Bonzini     flash_sync_area(s, offset, len);
65049ab747fSPaolo Bonzini }
65149ab747fSPaolo Bonzini 
flash_sync_dirty(Flash * s,int64_t newpage)65249ab747fSPaolo Bonzini static inline void flash_sync_dirty(Flash *s, int64_t newpage)
65349ab747fSPaolo Bonzini {
65449ab747fSPaolo Bonzini     if (s->dirty_page >= 0 && s->dirty_page != newpage) {
65549ab747fSPaolo Bonzini         flash_sync_page(s, s->dirty_page);
65649ab747fSPaolo Bonzini         s->dirty_page = newpage;
65749ab747fSPaolo Bonzini     }
65849ab747fSPaolo Bonzini }
65949ab747fSPaolo Bonzini 
66049ab747fSPaolo Bonzini static inline
flash_write8(Flash * s,uint32_t addr,uint8_t data)661b7f480c3SPaolo Bonzini void flash_write8(Flash *s, uint32_t addr, uint8_t data)
66249ab747fSPaolo Bonzini {
663b7f480c3SPaolo Bonzini     uint32_t page = addr / s->pi->page_size;
66449ab747fSPaolo Bonzini     uint8_t prev = s->storage[s->cur_addr];
6652113a128SIris Chen     uint32_t block_protect_value = (s->block_protect3 << 3) |
6662113a128SIris Chen                                    (s->block_protect2 << 2) |
6672113a128SIris Chen                                    (s->block_protect1 << 1) |
6682113a128SIris Chen                                    (s->block_protect0 << 0);
66949ab747fSPaolo Bonzini 
67049ab747fSPaolo Bonzini     if (!s->write_enable) {
671e9711b4dSPeter Crosthwaite         qemu_log_mask(LOG_GUEST_ERROR, "M25P80: write with write protect!\n");
6721695854bSBin Meng         return;
67349ab747fSPaolo Bonzini     }
67449ab747fSPaolo Bonzini 
6752113a128SIris Chen     if (block_protect_value > 0) {
6762113a128SIris Chen         uint32_t num_protected_sectors = 1 << (block_protect_value - 1);
6772113a128SIris Chen         uint32_t sector = addr / s->pi->sector_size;
6782113a128SIris Chen 
6792113a128SIris Chen         /* top_bottom_bit == 0 means TOP */
6802113a128SIris Chen         if (!s->top_bottom_bit) {
6812113a128SIris Chen             if (s->pi->n_sectors <= sector + num_protected_sectors) {
6822113a128SIris Chen                 qemu_log_mask(LOG_GUEST_ERROR,
6832113a128SIris Chen                               "M25P80: write with write protect!\n");
6842113a128SIris Chen                 return;
6852113a128SIris Chen             }
6862113a128SIris Chen         } else {
6872113a128SIris Chen             if (sector < num_protected_sectors) {
6882113a128SIris Chen                 qemu_log_mask(LOG_GUEST_ERROR,
6892113a128SIris Chen                               "M25P80: write with write protect!\n");
6902113a128SIris Chen                 return;
6912113a128SIris Chen             }
6922113a128SIris Chen         }
6932113a128SIris Chen     }
6942113a128SIris Chen 
69549ab747fSPaolo Bonzini     if ((prev ^ data) & data) {
696ccc46090SGuenter Roeck         trace_m25p80_programming_zero_to_one(s, addr, prev, data);
69749ab747fSPaolo Bonzini     }
69849ab747fSPaolo Bonzini 
6991435bcd6SMarcin Krzeminski     if (s->pi->flags & EEPROM) {
70049ab747fSPaolo Bonzini         s->storage[s->cur_addr] = data;
70149ab747fSPaolo Bonzini     } else {
70249ab747fSPaolo Bonzini         s->storage[s->cur_addr] &= data;
70349ab747fSPaolo Bonzini     }
70449ab747fSPaolo Bonzini 
70549ab747fSPaolo Bonzini     flash_sync_dirty(s, page);
70649ab747fSPaolo Bonzini     s->dirty_page = page;
70749ab747fSPaolo Bonzini }
70849ab747fSPaolo Bonzini 
get_addr_length(Flash * s)709c0f3f675SMarcin Krzeminski static inline int get_addr_length(Flash *s)
710c0f3f675SMarcin Krzeminski {
7111435bcd6SMarcin Krzeminski    /* check if eeprom is in use */
7121435bcd6SMarcin Krzeminski     if (s->pi->flags == EEPROM) {
7131435bcd6SMarcin Krzeminski         return 2;
7141435bcd6SMarcin Krzeminski     }
7151435bcd6SMarcin Krzeminski 
71663e47f6fSMarcin Krzeminski    switch (s->cmd_in_progress) {
7172389bcc2SCédric Le Goater    case RDSFDP:
7182389bcc2SCédric Le Goater        return 3;
71963e47f6fSMarcin Krzeminski    case PP4:
72030467afeSMarcin Krzeminski    case PP4_4:
721597c15f0SMarcin Krzeminski    case QPP_4:
72263e47f6fSMarcin Krzeminski    case READ4:
72363e47f6fSMarcin Krzeminski    case QIOR4:
72463e47f6fSMarcin Krzeminski    case ERASE4_4K:
72530467afeSMarcin Krzeminski    case ERASE4_32K:
72663e47f6fSMarcin Krzeminski    case ERASE4_SECTOR:
72763e47f6fSMarcin Krzeminski    case FAST_READ4:
72863e47f6fSMarcin Krzeminski    case DOR4:
72963e47f6fSMarcin Krzeminski    case QOR4:
73063e47f6fSMarcin Krzeminski    case DIOR4:
73163e47f6fSMarcin Krzeminski        return 4;
73263e47f6fSMarcin Krzeminski    default:
733c0f3f675SMarcin Krzeminski        return s->four_bytes_address_mode ? 4 : 3;
734c0f3f675SMarcin Krzeminski    }
73563e47f6fSMarcin Krzeminski }
736c0f3f675SMarcin Krzeminski 
complete_collecting_data(Flash * s)73749ab747fSPaolo Bonzini static void complete_collecting_data(Flash *s)
73849ab747fSPaolo Bonzini {
739b68cb060SPaolo Bonzini     int i, n;
740c0f3f675SMarcin Krzeminski 
741b68cb060SPaolo Bonzini     n = get_addr_length(s);
742b68cb060SPaolo Bonzini     s->cur_addr = (n == 3 ? s->ear : 0);
743b68cb060SPaolo Bonzini     for (i = 0; i < n; ++i) {
744c0f3f675SMarcin Krzeminski         s->cur_addr <<= 8;
745c0f3f675SMarcin Krzeminski         s->cur_addr |= s->data[i];
746c0f3f675SMarcin Krzeminski     }
747c0f3f675SMarcin Krzeminski 
748b68cb060SPaolo Bonzini     s->cur_addr &= s->size - 1;
74949ab747fSPaolo Bonzini 
75049ab747fSPaolo Bonzini     s->state = STATE_IDLE;
75149ab747fSPaolo Bonzini 
752ccc46090SGuenter Roeck     trace_m25p80_complete_collecting(s, s->cmd_in_progress, n, s->ear,
753ccc46090SGuenter Roeck                                      s->cur_addr);
754ccc46090SGuenter Roeck 
75549ab747fSPaolo Bonzini     switch (s->cmd_in_progress) {
75649ab747fSPaolo Bonzini     case DPP:
75749ab747fSPaolo Bonzini     case QPP:
758597c15f0SMarcin Krzeminski     case QPP_4:
75949ab747fSPaolo Bonzini     case PP:
76063e47f6fSMarcin Krzeminski     case PP4:
76130467afeSMarcin Krzeminski     case PP4_4:
76249ab747fSPaolo Bonzini         s->state = STATE_PAGE_PROGRAM;
76349ab747fSPaolo Bonzini         break;
764465ef47aSXuzhou Cheng     case AAI_WP:
765465ef47aSXuzhou Cheng         /* AAI programming starts from the even address */
766465ef47aSXuzhou Cheng         s->cur_addr &= ~BIT(0);
767465ef47aSXuzhou Cheng         s->state = STATE_PAGE_PROGRAM;
768465ef47aSXuzhou Cheng         break;
76949ab747fSPaolo Bonzini     case READ:
77063e47f6fSMarcin Krzeminski     case READ4:
77149ab747fSPaolo Bonzini     case FAST_READ:
77263e47f6fSMarcin Krzeminski     case FAST_READ4:
77349ab747fSPaolo Bonzini     case DOR:
77463e47f6fSMarcin Krzeminski     case DOR4:
77549ab747fSPaolo Bonzini     case QOR:
77663e47f6fSMarcin Krzeminski     case QOR4:
77749ab747fSPaolo Bonzini     case DIOR:
77863e47f6fSMarcin Krzeminski     case DIOR4:
77949ab747fSPaolo Bonzini     case QIOR:
78063e47f6fSMarcin Krzeminski     case QIOR4:
78149ab747fSPaolo Bonzini         s->state = STATE_READ;
78249ab747fSPaolo Bonzini         break;
78349ab747fSPaolo Bonzini     case ERASE_4K:
78463e47f6fSMarcin Krzeminski     case ERASE4_4K:
78549ab747fSPaolo Bonzini     case ERASE_32K:
78630467afeSMarcin Krzeminski     case ERASE4_32K:
78749ab747fSPaolo Bonzini     case ERASE_SECTOR:
78863e47f6fSMarcin Krzeminski     case ERASE4_SECTOR:
789f509dfeeSMarcin Krzeminski     case DIE_ERASE:
79049ab747fSPaolo Bonzini         flash_erase(s, s->cur_addr, s->cmd_in_progress);
79149ab747fSPaolo Bonzini         break;
79249ab747fSPaolo Bonzini     case WRSR:
7932fa22a0fSIris Chen         s->status_register_write_disabled = extract32(s->data[0], 7, 1);
7942113a128SIris Chen         s->block_protect0 = extract32(s->data[0], 2, 1);
7952113a128SIris Chen         s->block_protect1 = extract32(s->data[0], 3, 1);
7962113a128SIris Chen         s->block_protect2 = extract32(s->data[0], 4, 1);
7972113a128SIris Chen         if (s->pi->flags & HAS_SR_TB) {
7982113a128SIris Chen             s->top_bottom_bit = extract32(s->data[0], 5, 1);
7992113a128SIris Chen         }
8002113a128SIris Chen         if (s->pi->flags & HAS_SR_BP3_BIT6) {
8012113a128SIris Chen             s->block_protect3 = extract32(s->data[0], 6, 1);
8022113a128SIris Chen         }
8032fa22a0fSIris Chen 
8047a69c100SMarcin Krzeminski         switch (get_man(s)) {
8057a69c100SMarcin Krzeminski         case MAN_SPANSION:
8067a69c100SMarcin Krzeminski             s->quad_enable = !!(s->data[1] & 0x02);
8077a69c100SMarcin Krzeminski             break;
80810509e10SBin Meng         case MAN_ISSI:
80910509e10SBin Meng             s->quad_enable = extract32(s->data[0], 6, 1);
81010509e10SBin Meng             break;
8117a69c100SMarcin Krzeminski         case MAN_MACRONIX:
8127a69c100SMarcin Krzeminski             s->quad_enable = extract32(s->data[0], 6, 1);
813d9cc8701SMarcin Krzeminski             if (s->len > 1) {
8142151b044SCédric Le Goater                 s->volatile_cfg = s->data[1];
815d9cc8701SMarcin Krzeminski                 s->four_bytes_address_mode = extract32(s->data[1], 5, 1);
816d9cc8701SMarcin Krzeminski             }
8177a69c100SMarcin Krzeminski             break;
81861f93767SJamin Lin         case MAN_WINBOND:
81961f93767SJamin Lin             if (s->len > 1) {
82061f93767SJamin Lin                 s->quad_enable = !!(s->data[1] & 0x02);
82161f93767SJamin Lin             }
82261f93767SJamin Lin             break;
8237a69c100SMarcin Krzeminski         default:
8247a69c100SMarcin Krzeminski             break;
8257a69c100SMarcin Krzeminski         }
82649ab747fSPaolo Bonzini         if (s->write_enable) {
82749ab747fSPaolo Bonzini             s->write_enable = false;
82849ab747fSPaolo Bonzini         }
82949ab747fSPaolo Bonzini         break;
8309785731eSJamin Lin     case WRSR2:
8319785731eSJamin Lin         switch (get_man(s)) {
8329785731eSJamin Lin         case MAN_WINBOND:
8339785731eSJamin Lin             s->quad_enable = !!(s->data[0] & 0x02);
8349785731eSJamin Lin             break;
8359785731eSJamin Lin         default:
8369785731eSJamin Lin             break;
8379785731eSJamin Lin         }
8389785731eSJamin Lin         break;
8390f589782SFrancisco Iglesias     case BRWR:
840d8a29a7aSMarcin Krzeminski     case EXTEND_ADDR_WRITE:
841d8a29a7aSMarcin Krzeminski         s->ear = s->data[0];
842d8a29a7aSMarcin Krzeminski         break;
843cb475951SMarcin Krzeminski     case WNVCR:
844cb475951SMarcin Krzeminski         s->nonvolatile_cfg = s->data[0] | (s->data[1] << 8);
845cb475951SMarcin Krzeminski         break;
846cb475951SMarcin Krzeminski     case WVCR:
847cb475951SMarcin Krzeminski         s->volatile_cfg = s->data[0];
848cb475951SMarcin Krzeminski         break;
849cb475951SMarcin Krzeminski     case WEVCR:
850cb475951SMarcin Krzeminski         s->enh_volatile_cfg = s->data[0];
851cb475951SMarcin Krzeminski         break;
852a87fc364SFrancisco Iglesias     case RDID_90:
853a87fc364SFrancisco Iglesias     case RDID_AB:
854a87fc364SFrancisco Iglesias         if (get_man(s) == MAN_SST) {
855a87fc364SFrancisco Iglesias             if (s->cur_addr <= 1) {
856a87fc364SFrancisco Iglesias                 if (s->cur_addr) {
857a87fc364SFrancisco Iglesias                     s->data[0] = s->pi->id[2];
858a87fc364SFrancisco Iglesias                     s->data[1] = s->pi->id[0];
859a87fc364SFrancisco Iglesias                 } else {
860a87fc364SFrancisco Iglesias                     s->data[0] = s->pi->id[0];
861a87fc364SFrancisco Iglesias                     s->data[1] = s->pi->id[2];
862a87fc364SFrancisco Iglesias                 }
863a87fc364SFrancisco Iglesias                 s->pos = 0;
864a87fc364SFrancisco Iglesias                 s->len = 2;
865a87fc364SFrancisco Iglesias                 s->data_read_loop = true;
866a87fc364SFrancisco Iglesias                 s->state = STATE_READING_DATA;
867a87fc364SFrancisco Iglesias             } else {
868a87fc364SFrancisco Iglesias                 qemu_log_mask(LOG_GUEST_ERROR,
869a87fc364SFrancisco Iglesias                               "M25P80: Invalid read id address\n");
870a87fc364SFrancisco Iglesias             }
871a87fc364SFrancisco Iglesias         } else {
872a87fc364SFrancisco Iglesias             qemu_log_mask(LOG_GUEST_ERROR,
873a87fc364SFrancisco Iglesias                           "M25P80: Read id (command 0x90/0xAB) is not supported"
874a87fc364SFrancisco Iglesias                           " by device\n");
875a87fc364SFrancisco Iglesias         }
876a87fc364SFrancisco Iglesias         break;
8772389bcc2SCédric Le Goater 
8782389bcc2SCédric Le Goater     case RDSFDP:
8792389bcc2SCédric Le Goater         s->state = STATE_READING_SFDP;
8802389bcc2SCédric Le Goater         break;
8812389bcc2SCédric Le Goater 
88249ab747fSPaolo Bonzini     default:
88349ab747fSPaolo Bonzini         break;
88449ab747fSPaolo Bonzini     }
88549ab747fSPaolo Bonzini }
88649ab747fSPaolo Bonzini 
reset_memory(Flash * s)887187c2636SMarcin Krzeminski static void reset_memory(Flash *s)
888187c2636SMarcin Krzeminski {
889187c2636SMarcin Krzeminski     s->cmd_in_progress = NOP;
890187c2636SMarcin Krzeminski     s->cur_addr = 0;
891d8a29a7aSMarcin Krzeminski     s->ear = 0;
892c0f3f675SMarcin Krzeminski     s->four_bytes_address_mode = false;
893187c2636SMarcin Krzeminski     s->len = 0;
894187c2636SMarcin Krzeminski     s->needed_bytes = 0;
895187c2636SMarcin Krzeminski     s->pos = 0;
896187c2636SMarcin Krzeminski     s->state = STATE_IDLE;
897187c2636SMarcin Krzeminski     s->write_enable = false;
898187c2636SMarcin Krzeminski     s->reset_enable = false;
8997a69c100SMarcin Krzeminski     s->quad_enable = false;
900465ef47aSXuzhou Cheng     s->aai_enable = false;
901187c2636SMarcin Krzeminski 
902c7cd0a6cSMarcin Krzeminski     switch (get_man(s)) {
903c7cd0a6cSMarcin Krzeminski     case MAN_NUMONYX:
904cb475951SMarcin Krzeminski         s->volatile_cfg = 0;
905cb475951SMarcin Krzeminski         s->volatile_cfg |= VCFG_DUMMY;
906cb475951SMarcin Krzeminski         s->volatile_cfg |= VCFG_WRAP_SEQUENTIAL;
907cb475951SMarcin Krzeminski         if ((s->nonvolatile_cfg & NVCFG_XIP_MODE_MASK)
908fc5df349SJoe Komlodi                                 == NVCFG_XIP_MODE_DISABLED) {
90909414144SJoe Komlodi             s->volatile_cfg |= VCFG_XIP_MODE_DISABLED;
910cb475951SMarcin Krzeminski         }
911cb475951SMarcin Krzeminski         s->volatile_cfg |= deposit32(s->volatile_cfg,
912cb475951SMarcin Krzeminski                             VCFG_DUMMY_CLK_POS,
913cb475951SMarcin Krzeminski                             CFG_DUMMY_CLK_LEN,
914cb475951SMarcin Krzeminski                             extract32(s->nonvolatile_cfg,
915cb475951SMarcin Krzeminski                                         NVCFG_DUMMY_CLK_POS,
916cb475951SMarcin Krzeminski                                         CFG_DUMMY_CLK_LEN)
917cb475951SMarcin Krzeminski                             );
918cb475951SMarcin Krzeminski 
919cb475951SMarcin Krzeminski         s->enh_volatile_cfg = 0;
9205c765e7aSStefan Weil         s->enh_volatile_cfg |= EVCFG_OUT_DRIVER_STRENGTH_DEF;
921cb475951SMarcin Krzeminski         s->enh_volatile_cfg |= EVCFG_VPP_ACCELERATOR;
922cb475951SMarcin Krzeminski         s->enh_volatile_cfg |= EVCFG_RESET_HOLD_ENABLED;
923cb475951SMarcin Krzeminski         if (s->nonvolatile_cfg & NVCFG_DUAL_IO_MASK) {
92409414144SJoe Komlodi             s->enh_volatile_cfg |= EVCFG_DUAL_IO_DISABLED;
925cb475951SMarcin Krzeminski         }
926cb475951SMarcin Krzeminski         if (s->nonvolatile_cfg & NVCFG_QUAD_IO_MASK) {
92709414144SJoe Komlodi             s->enh_volatile_cfg |= EVCFG_QUAD_IO_DISABLED;
928cb475951SMarcin Krzeminski         }
929cb475951SMarcin Krzeminski         if (!(s->nonvolatile_cfg & NVCFG_4BYTE_ADDR_MASK)) {
930cb475951SMarcin Krzeminski             s->four_bytes_address_mode = true;
931cb475951SMarcin Krzeminski         }
932cb475951SMarcin Krzeminski         if (!(s->nonvolatile_cfg & NVCFG_LOWER_SEGMENT_MASK)) {
933e02b3bf2SMarcin Krzeminski             s->ear = s->size / MAX_3BYTES_SIZE - 1;
934cb475951SMarcin Krzeminski         }
935c7cd0a6cSMarcin Krzeminski         break;
936d9cc8701SMarcin Krzeminski     case MAN_MACRONIX:
937d9cc8701SMarcin Krzeminski         s->volatile_cfg = 0x7;
938d9cc8701SMarcin Krzeminski         break;
939d9cc8701SMarcin Krzeminski     case MAN_SPANSION:
940d9cc8701SMarcin Krzeminski         s->spansion_cr1v = s->spansion_cr1nv;
941d9cc8701SMarcin Krzeminski         s->spansion_cr2v = s->spansion_cr2nv;
942d9cc8701SMarcin Krzeminski         s->spansion_cr3v = s->spansion_cr3nv;
943d9cc8701SMarcin Krzeminski         s->spansion_cr4v = s->spansion_cr4nv;
944d9cc8701SMarcin Krzeminski         s->quad_enable = extract32(s->spansion_cr1v,
945d9cc8701SMarcin Krzeminski                                    SPANSION_QUAD_CFG_POS,
946d9cc8701SMarcin Krzeminski                                    SPANSION_QUAD_CFG_LEN
947d9cc8701SMarcin Krzeminski                                    );
948d9cc8701SMarcin Krzeminski         s->four_bytes_address_mode = extract32(s->spansion_cr2v,
949d9cc8701SMarcin Krzeminski                 SPANSION_ADDR_LEN_POS,
950d9cc8701SMarcin Krzeminski                 SPANSION_ADDR_LEN_LEN
951d9cc8701SMarcin Krzeminski                 );
952d9cc8701SMarcin Krzeminski         break;
953c7cd0a6cSMarcin Krzeminski     default:
954c7cd0a6cSMarcin Krzeminski         break;
955cb475951SMarcin Krzeminski     }
956cb475951SMarcin Krzeminski 
957ccc46090SGuenter Roeck     trace_m25p80_reset_done(s);
958187c2636SMarcin Krzeminski }
959187c2636SMarcin Krzeminski 
numonyx_mode(Flash * s)96023486231SJoe Komlodi static uint8_t numonyx_mode(Flash *s)
96123486231SJoe Komlodi {
96223486231SJoe Komlodi     if (!(s->enh_volatile_cfg & EVCFG_QUAD_IO_DISABLED)) {
96323486231SJoe Komlodi         return MODE_QIO;
96423486231SJoe Komlodi     } else if (!(s->enh_volatile_cfg & EVCFG_DUAL_IO_DISABLED)) {
96523486231SJoe Komlodi         return MODE_DIO;
96623486231SJoe Komlodi     } else {
96723486231SJoe Komlodi         return MODE_STD;
96823486231SJoe Komlodi     }
96923486231SJoe Komlodi }
97023486231SJoe Komlodi 
numonyx_extract_cfg_num_dummies(Flash * s)97123af2685SJoe Komlodi static uint8_t numonyx_extract_cfg_num_dummies(Flash *s)
97223af2685SJoe Komlodi {
97323af2685SJoe Komlodi     uint8_t num_dummies;
97423af2685SJoe Komlodi     uint8_t mode;
97523af2685SJoe Komlodi     assert(get_man(s) == MAN_NUMONYX);
97623af2685SJoe Komlodi 
97723af2685SJoe Komlodi     mode = numonyx_mode(s);
97823af2685SJoe Komlodi     num_dummies = extract32(s->volatile_cfg, 4, 4);
97923af2685SJoe Komlodi 
98023af2685SJoe Komlodi     if (num_dummies == 0x0 || num_dummies == 0xf) {
98123af2685SJoe Komlodi         switch (s->cmd_in_progress) {
98223af2685SJoe Komlodi         case QIOR:
98323af2685SJoe Komlodi         case QIOR4:
98423af2685SJoe Komlodi             num_dummies = 10;
98523af2685SJoe Komlodi             break;
98623af2685SJoe Komlodi         default:
98723af2685SJoe Komlodi             num_dummies = (mode == MODE_QIO) ? 10 : 8;
98823af2685SJoe Komlodi             break;
98923af2685SJoe Komlodi         }
99023af2685SJoe Komlodi     }
99123af2685SJoe Komlodi 
99223af2685SJoe Komlodi     return num_dummies;
99323af2685SJoe Komlodi }
99423af2685SJoe Komlodi 
decode_fast_read_cmd(Flash * s)995cf6f1efeSMarcin Krzeminski static void decode_fast_read_cmd(Flash *s)
996cf6f1efeSMarcin Krzeminski {
997cf6f1efeSMarcin Krzeminski     s->needed_bytes = get_addr_length(s);
998cf6f1efeSMarcin Krzeminski     switch (get_man(s)) {
999cf6f1efeSMarcin Krzeminski     /* Dummy cycles - modeled with bytes writes instead of bits */
1000aac8e46eSBin Meng     case MAN_SST:
1001aac8e46eSBin Meng         s->needed_bytes += 1;
1002aac8e46eSBin Meng         break;
10033830c7a4SMarcin Krzeminski     case MAN_WINBOND:
10043830c7a4SMarcin Krzeminski         s->needed_bytes += 8;
10053830c7a4SMarcin Krzeminski         break;
1006cf6f1efeSMarcin Krzeminski     case MAN_NUMONYX:
100723af2685SJoe Komlodi         s->needed_bytes += numonyx_extract_cfg_num_dummies(s);
1008cf6f1efeSMarcin Krzeminski         break;
1009cf6f1efeSMarcin Krzeminski     case MAN_MACRONIX:
1010cf6f1efeSMarcin Krzeminski         if (extract32(s->volatile_cfg, 6, 2) == 1) {
1011cf6f1efeSMarcin Krzeminski             s->needed_bytes += 6;
1012cf6f1efeSMarcin Krzeminski         } else {
1013cf6f1efeSMarcin Krzeminski             s->needed_bytes += 8;
1014cf6f1efeSMarcin Krzeminski         }
1015cf6f1efeSMarcin Krzeminski         break;
1016cf6f1efeSMarcin Krzeminski     case MAN_SPANSION:
1017cf6f1efeSMarcin Krzeminski         s->needed_bytes += extract32(s->spansion_cr2v,
1018cf6f1efeSMarcin Krzeminski                                     SPANSION_DUMMY_CLK_POS,
1019cf6f1efeSMarcin Krzeminski                                     SPANSION_DUMMY_CLK_LEN
1020cf6f1efeSMarcin Krzeminski                                     );
1021cf6f1efeSMarcin Krzeminski         break;
102210509e10SBin Meng     case MAN_ISSI:
102310509e10SBin Meng         /*
102410509e10SBin Meng          * The Fast Read instruction code is followed by address bytes and
102510509e10SBin Meng          * dummy cycles, transmitted via the SI line.
102610509e10SBin Meng          *
102710509e10SBin Meng          * The number of dummy cycles is configurable but this is currently
102810509e10SBin Meng          * unmodeled, hence the default value 8 is used.
102910509e10SBin Meng          *
103010509e10SBin Meng          * QPI (Quad Peripheral Interface) mode has different default value
103110509e10SBin Meng          * of dummy cycles, but this is unsupported at the time being.
103210509e10SBin Meng          */
103310509e10SBin Meng         s->needed_bytes += 1;
103410509e10SBin Meng         break;
1035cf6f1efeSMarcin Krzeminski     default:
1036cf6f1efeSMarcin Krzeminski         break;
1037cf6f1efeSMarcin Krzeminski     }
1038cf6f1efeSMarcin Krzeminski     s->pos = 0;
1039cf6f1efeSMarcin Krzeminski     s->len = 0;
1040cf6f1efeSMarcin Krzeminski     s->state = STATE_COLLECTING_DATA;
1041cf6f1efeSMarcin Krzeminski }
1042cf6f1efeSMarcin Krzeminski 
decode_dio_read_cmd(Flash * s)1043cf6f1efeSMarcin Krzeminski static void decode_dio_read_cmd(Flash *s)
1044cf6f1efeSMarcin Krzeminski {
1045cf6f1efeSMarcin Krzeminski     s->needed_bytes = get_addr_length(s);
1046cf6f1efeSMarcin Krzeminski     /* Dummy cycles modeled with bytes writes instead of bits */
1047cf6f1efeSMarcin Krzeminski     switch (get_man(s)) {
1048cf6f1efeSMarcin Krzeminski     case MAN_WINBOND:
1049fe847705SMarcin Krzeminski         s->needed_bytes += WINBOND_CONTINUOUS_READ_MODE_CMD_LEN;
1050cf6f1efeSMarcin Krzeminski         break;
1051cf6f1efeSMarcin Krzeminski     case MAN_SPANSION:
1052cf6f1efeSMarcin Krzeminski         s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN;
1053cf6f1efeSMarcin Krzeminski         s->needed_bytes += extract32(s->spansion_cr2v,
1054cf6f1efeSMarcin Krzeminski                                     SPANSION_DUMMY_CLK_POS,
1055cf6f1efeSMarcin Krzeminski                                     SPANSION_DUMMY_CLK_LEN
1056cf6f1efeSMarcin Krzeminski                                     );
1057cf6f1efeSMarcin Krzeminski         break;
1058cf6f1efeSMarcin Krzeminski     case MAN_NUMONYX:
105923af2685SJoe Komlodi         s->needed_bytes += numonyx_extract_cfg_num_dummies(s);
1060cf6f1efeSMarcin Krzeminski         break;
1061cf6f1efeSMarcin Krzeminski     case MAN_MACRONIX:
1062cf6f1efeSMarcin Krzeminski         switch (extract32(s->volatile_cfg, 6, 2)) {
1063cf6f1efeSMarcin Krzeminski         case 1:
1064cf6f1efeSMarcin Krzeminski             s->needed_bytes += 6;
1065cf6f1efeSMarcin Krzeminski             break;
1066cf6f1efeSMarcin Krzeminski         case 2:
1067cf6f1efeSMarcin Krzeminski             s->needed_bytes += 8;
1068cf6f1efeSMarcin Krzeminski             break;
1069cf6f1efeSMarcin Krzeminski         default:
1070cf6f1efeSMarcin Krzeminski             s->needed_bytes += 4;
1071cf6f1efeSMarcin Krzeminski             break;
1072cf6f1efeSMarcin Krzeminski         }
1073cf6f1efeSMarcin Krzeminski         break;
107410509e10SBin Meng     case MAN_ISSI:
107510509e10SBin Meng         /*
107610509e10SBin Meng          * The Fast Read Dual I/O instruction code is followed by address bytes
107710509e10SBin Meng          * and dummy cycles, transmitted via the IO1 and IO0 line.
107810509e10SBin Meng          *
107910509e10SBin Meng          * The number of dummy cycles is configurable but this is currently
108010509e10SBin Meng          * unmodeled, hence the default value 4 is used.
108110509e10SBin Meng          */
108210509e10SBin Meng         s->needed_bytes += 1;
108310509e10SBin Meng         break;
1084cf6f1efeSMarcin Krzeminski     default:
1085cf6f1efeSMarcin Krzeminski         break;
1086cf6f1efeSMarcin Krzeminski     }
1087cf6f1efeSMarcin Krzeminski     s->pos = 0;
1088cf6f1efeSMarcin Krzeminski     s->len = 0;
1089cf6f1efeSMarcin Krzeminski     s->state = STATE_COLLECTING_DATA;
1090cf6f1efeSMarcin Krzeminski }
1091cf6f1efeSMarcin Krzeminski 
decode_qio_read_cmd(Flash * s)1092cf6f1efeSMarcin Krzeminski static void decode_qio_read_cmd(Flash *s)
1093cf6f1efeSMarcin Krzeminski {
1094cf6f1efeSMarcin Krzeminski     s->needed_bytes = get_addr_length(s);
1095cf6f1efeSMarcin Krzeminski     /* Dummy cycles modeled with bytes writes instead of bits */
1096cf6f1efeSMarcin Krzeminski     switch (get_man(s)) {
1097cf6f1efeSMarcin Krzeminski     case MAN_WINBOND:
1098fe847705SMarcin Krzeminski         s->needed_bytes += WINBOND_CONTINUOUS_READ_MODE_CMD_LEN;
1099fe847705SMarcin Krzeminski         s->needed_bytes += 4;
1100cf6f1efeSMarcin Krzeminski         break;
1101cf6f1efeSMarcin Krzeminski     case MAN_SPANSION:
1102cf6f1efeSMarcin Krzeminski         s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN;
1103cf6f1efeSMarcin Krzeminski         s->needed_bytes += extract32(s->spansion_cr2v,
1104cf6f1efeSMarcin Krzeminski                                     SPANSION_DUMMY_CLK_POS,
1105cf6f1efeSMarcin Krzeminski                                     SPANSION_DUMMY_CLK_LEN
1106cf6f1efeSMarcin Krzeminski                                     );
1107cf6f1efeSMarcin Krzeminski         break;
1108cf6f1efeSMarcin Krzeminski     case MAN_NUMONYX:
110923af2685SJoe Komlodi         s->needed_bytes += numonyx_extract_cfg_num_dummies(s);
1110cf6f1efeSMarcin Krzeminski         break;
1111cf6f1efeSMarcin Krzeminski     case MAN_MACRONIX:
1112cf6f1efeSMarcin Krzeminski         switch (extract32(s->volatile_cfg, 6, 2)) {
1113cf6f1efeSMarcin Krzeminski         case 1:
1114cf6f1efeSMarcin Krzeminski             s->needed_bytes += 4;
1115cf6f1efeSMarcin Krzeminski             break;
1116cf6f1efeSMarcin Krzeminski         case 2:
1117cf6f1efeSMarcin Krzeminski             s->needed_bytes += 8;
1118cf6f1efeSMarcin Krzeminski             break;
1119cf6f1efeSMarcin Krzeminski         default:
1120cf6f1efeSMarcin Krzeminski             s->needed_bytes += 6;
1121cf6f1efeSMarcin Krzeminski             break;
1122cf6f1efeSMarcin Krzeminski         }
1123cf6f1efeSMarcin Krzeminski         break;
112410509e10SBin Meng     case MAN_ISSI:
112510509e10SBin Meng         /*
112610509e10SBin Meng          * The Fast Read Quad I/O instruction code is followed by address bytes
112710509e10SBin Meng          * and dummy cycles, transmitted via the IO3, IO2, IO1 and IO0 line.
112810509e10SBin Meng          *
112910509e10SBin Meng          * The number of dummy cycles is configurable but this is currently
113010509e10SBin Meng          * unmodeled, hence the default value 6 is used.
113110509e10SBin Meng          *
113210509e10SBin Meng          * QPI (Quad Peripheral Interface) mode has different default value
113310509e10SBin Meng          * of dummy cycles, but this is unsupported at the time being.
113410509e10SBin Meng          */
113510509e10SBin Meng         s->needed_bytes += 3;
113610509e10SBin Meng         break;
1137cf6f1efeSMarcin Krzeminski     default:
1138cf6f1efeSMarcin Krzeminski         break;
1139cf6f1efeSMarcin Krzeminski     }
1140cf6f1efeSMarcin Krzeminski     s->pos = 0;
1141cf6f1efeSMarcin Krzeminski     s->len = 0;
1142cf6f1efeSMarcin Krzeminski     s->state = STATE_COLLECTING_DATA;
1143cf6f1efeSMarcin Krzeminski }
1144cf6f1efeSMarcin Krzeminski 
is_valid_aai_cmd(uint32_t cmd)1145465ef47aSXuzhou Cheng static bool is_valid_aai_cmd(uint32_t cmd)
1146465ef47aSXuzhou Cheng {
1147465ef47aSXuzhou Cheng     return cmd == AAI_WP || cmd == WRDI || cmd == RDSR;
1148465ef47aSXuzhou Cheng }
1149465ef47aSXuzhou Cheng 
decode_new_cmd(Flash * s,uint32_t value)115049ab747fSPaolo Bonzini static void decode_new_cmd(Flash *s, uint32_t value)
115149ab747fSPaolo Bonzini {
1152e3ba6cd6SMarcin Krzeminski     int i;
1153ccc46090SGuenter Roeck 
1154ccc46090SGuenter Roeck     s->cmd_in_progress = value;
1155ccc46090SGuenter Roeck     trace_m25p80_command_decoded(s, value);
115649ab747fSPaolo Bonzini 
1157187c2636SMarcin Krzeminski     if (value != RESET_MEMORY) {
1158187c2636SMarcin Krzeminski         s->reset_enable = false;
1159187c2636SMarcin Krzeminski     }
1160187c2636SMarcin Krzeminski 
1161465ef47aSXuzhou Cheng     if (get_man(s) == MAN_SST && s->aai_enable && !is_valid_aai_cmd(value)) {
1162465ef47aSXuzhou Cheng         qemu_log_mask(LOG_GUEST_ERROR,
1163465ef47aSXuzhou Cheng                       "M25P80: Invalid cmd within AAI programming sequence");
1164465ef47aSXuzhou Cheng     }
1165465ef47aSXuzhou Cheng 
116649ab747fSPaolo Bonzini     switch (value) {
116749ab747fSPaolo Bonzini 
116849ab747fSPaolo Bonzini     case ERASE_4K:
116963e47f6fSMarcin Krzeminski     case ERASE4_4K:
117049ab747fSPaolo Bonzini     case ERASE_32K:
117130467afeSMarcin Krzeminski     case ERASE4_32K:
117249ab747fSPaolo Bonzini     case ERASE_SECTOR:
117363e47f6fSMarcin Krzeminski     case ERASE4_SECTOR:
117449ab747fSPaolo Bonzini     case PP:
117563e47f6fSMarcin Krzeminski     case PP4:
1176f509dfeeSMarcin Krzeminski     case DIE_ERASE:
1177a87fc364SFrancisco Iglesias     case RDID_90:
1178a87fc364SFrancisco Iglesias     case RDID_AB:
1179c0f3f675SMarcin Krzeminski         s->needed_bytes = get_addr_length(s);
118049ab747fSPaolo Bonzini         s->pos = 0;
118149ab747fSPaolo Bonzini         s->len = 0;
118249ab747fSPaolo Bonzini         s->state = STATE_COLLECTING_DATA;
118349ab747fSPaolo Bonzini         break;
118423486231SJoe Komlodi     case READ:
118523486231SJoe Komlodi     case READ4:
118623486231SJoe Komlodi         if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) == MODE_STD) {
118723486231SJoe Komlodi             s->needed_bytes = get_addr_length(s);
118823486231SJoe Komlodi             s->pos = 0;
118923486231SJoe Komlodi             s->len = 0;
119023486231SJoe Komlodi             s->state = STATE_COLLECTING_DATA;
119123486231SJoe Komlodi         } else {
119223486231SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Cannot execute cmd %x in "
119323486231SJoe Komlodi                           "DIO or QIO mode\n", s->cmd_in_progress);
119423486231SJoe Komlodi         }
119523486231SJoe Komlodi         break;
119623486231SJoe Komlodi     case DPP:
119723486231SJoe Komlodi         if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) != MODE_QIO) {
119823486231SJoe Komlodi             s->needed_bytes = get_addr_length(s);
119923486231SJoe Komlodi             s->pos = 0;
120023486231SJoe Komlodi             s->len = 0;
120123486231SJoe Komlodi             s->state = STATE_COLLECTING_DATA;
120223486231SJoe Komlodi         } else {
120323486231SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Cannot execute cmd %x in "
120423486231SJoe Komlodi                           "QIO mode\n", s->cmd_in_progress);
120523486231SJoe Komlodi         }
120623486231SJoe Komlodi         break;
120723486231SJoe Komlodi     case QPP:
120823486231SJoe Komlodi     case QPP_4:
120923486231SJoe Komlodi     case PP4_4:
121023486231SJoe Komlodi         if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) != MODE_DIO) {
121123486231SJoe Komlodi             s->needed_bytes = get_addr_length(s);
121223486231SJoe Komlodi             s->pos = 0;
121323486231SJoe Komlodi             s->len = 0;
121423486231SJoe Komlodi             s->state = STATE_COLLECTING_DATA;
121523486231SJoe Komlodi         } else {
121623486231SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Cannot execute cmd %x in "
121723486231SJoe Komlodi                           "DIO mode\n", s->cmd_in_progress);
121823486231SJoe Komlodi         }
121923486231SJoe Komlodi         break;
122049ab747fSPaolo Bonzini 
122149ab747fSPaolo Bonzini     case FAST_READ:
122263e47f6fSMarcin Krzeminski     case FAST_READ4:
122323486231SJoe Komlodi         decode_fast_read_cmd(s);
122423486231SJoe Komlodi         break;
122549ab747fSPaolo Bonzini     case DOR:
122663e47f6fSMarcin Krzeminski     case DOR4:
122723486231SJoe Komlodi         if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) != MODE_QIO) {
122823486231SJoe Komlodi             decode_fast_read_cmd(s);
122923486231SJoe Komlodi         } else {
123023486231SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Cannot execute cmd %x in "
123123486231SJoe Komlodi                           "QIO mode\n", s->cmd_in_progress);
123223486231SJoe Komlodi         }
123323486231SJoe Komlodi         break;
123449ab747fSPaolo Bonzini     case QOR:
123563e47f6fSMarcin Krzeminski     case QOR4:
123623486231SJoe Komlodi         if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) != MODE_DIO) {
1237cf6f1efeSMarcin Krzeminski             decode_fast_read_cmd(s);
123823486231SJoe Komlodi         } else {
123923486231SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Cannot execute cmd %x in "
124023486231SJoe Komlodi                           "DIO mode\n", s->cmd_in_progress);
124123486231SJoe Komlodi         }
124249ab747fSPaolo Bonzini         break;
124349ab747fSPaolo Bonzini 
124449ab747fSPaolo Bonzini     case DIOR:
124563e47f6fSMarcin Krzeminski     case DIOR4:
124623486231SJoe Komlodi         if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) != MODE_QIO) {
1247cf6f1efeSMarcin Krzeminski             decode_dio_read_cmd(s);
124823486231SJoe Komlodi         } else {
124923486231SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Cannot execute cmd %x in "
125023486231SJoe Komlodi                           "QIO mode\n", s->cmd_in_progress);
125123486231SJoe Komlodi         }
125249ab747fSPaolo Bonzini         break;
125349ab747fSPaolo Bonzini 
125449ab747fSPaolo Bonzini     case QIOR:
125563e47f6fSMarcin Krzeminski     case QIOR4:
125623486231SJoe Komlodi         if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) != MODE_DIO) {
1257cf6f1efeSMarcin Krzeminski             decode_qio_read_cmd(s);
125823486231SJoe Komlodi         } else {
125923486231SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Cannot execute cmd %x in "
126023486231SJoe Komlodi                           "DIO mode\n", s->cmd_in_progress);
126123486231SJoe Komlodi         }
126249ab747fSPaolo Bonzini         break;
126349ab747fSPaolo Bonzini 
126449ab747fSPaolo Bonzini     case WRSR:
12652fa22a0fSIris Chen         /*
12662fa22a0fSIris Chen          * If WP# is low and status_register_write_disabled is high,
12672fa22a0fSIris Chen          * status register writes are disabled.
12682fa22a0fSIris Chen          * This is also called "hardware protected mode" (HPM). All other
12692fa22a0fSIris Chen          * combinations of the two states are called "software protected mode"
12702fa22a0fSIris Chen          * (SPM), and status register writes are permitted.
12712fa22a0fSIris Chen          */
12722fa22a0fSIris Chen         if ((s->wp_level == 0 && s->status_register_write_disabled)
12732fa22a0fSIris Chen             || !s->write_enable) {
12742fa22a0fSIris Chen             qemu_log_mask(LOG_GUEST_ERROR,
12752fa22a0fSIris Chen                           "M25P80: Status register write is disabled!\n");
12762fa22a0fSIris Chen             break;
12772fa22a0fSIris Chen         }
12782fa22a0fSIris Chen 
12797a69c100SMarcin Krzeminski         switch (get_man(s)) {
12807a69c100SMarcin Krzeminski         case MAN_SPANSION:
12817a69c100SMarcin Krzeminski             s->needed_bytes = 2;
128249ab747fSPaolo Bonzini             s->state = STATE_COLLECTING_DATA;
12837a69c100SMarcin Krzeminski             break;
12847a69c100SMarcin Krzeminski         case MAN_MACRONIX:
12857a69c100SMarcin Krzeminski             s->needed_bytes = 2;
12867a69c100SMarcin Krzeminski             s->state = STATE_COLLECTING_VAR_LEN_DATA;
12877a69c100SMarcin Krzeminski             break;
128861f93767SJamin Lin         case MAN_WINBOND:
128961f93767SJamin Lin             s->needed_bytes = 2;
129061f93767SJamin Lin             s->state = STATE_COLLECTING_VAR_LEN_DATA;
129161f93767SJamin Lin             break;
12927a69c100SMarcin Krzeminski         default:
12937a69c100SMarcin Krzeminski             s->needed_bytes = 1;
12947a69c100SMarcin Krzeminski             s->state = STATE_COLLECTING_DATA;
12957a69c100SMarcin Krzeminski         }
12967a69c100SMarcin Krzeminski         s->pos = 0;
129749ab747fSPaolo Bonzini         break;
12989785731eSJamin Lin     case WRSR2:
12999785731eSJamin Lin         /*
13009785731eSJamin Lin          * If WP# is low and status_register_write_disabled is high,
13019785731eSJamin Lin          * status register writes are disabled.
13029785731eSJamin Lin          * This is also called "hardware protected mode" (HPM). All other
13039785731eSJamin Lin          * combinations of the two states are called "software protected mode"
13049785731eSJamin Lin          * (SPM), and status register writes are permitted.
13059785731eSJamin Lin          */
13069785731eSJamin Lin         if ((s->wp_level == 0 && s->status_register_write_disabled)
13079785731eSJamin Lin             || !s->write_enable) {
13089785731eSJamin Lin             qemu_log_mask(LOG_GUEST_ERROR,
13099785731eSJamin Lin                           "M25P80: Status register 2 write is disabled!\n");
13109785731eSJamin Lin             break;
13119785731eSJamin Lin         }
131249ab747fSPaolo Bonzini 
13139785731eSJamin Lin         switch (get_man(s)) {
13149785731eSJamin Lin         case MAN_WINBOND:
13159785731eSJamin Lin             s->needed_bytes = 1;
13169785731eSJamin Lin             s->state = STATE_COLLECTING_DATA;
13179785731eSJamin Lin             s->pos = 0;
13189785731eSJamin Lin             break;
13199785731eSJamin Lin         default:
13209785731eSJamin Lin             break;
13219785731eSJamin Lin         }
13229785731eSJamin Lin         break;
132349ab747fSPaolo Bonzini     case WRDI:
132449ab747fSPaolo Bonzini         s->write_enable = false;
1325465ef47aSXuzhou Cheng         if (get_man(s) == MAN_SST) {
1326465ef47aSXuzhou Cheng             s->aai_enable = false;
1327465ef47aSXuzhou Cheng         }
132849ab747fSPaolo Bonzini         break;
132949ab747fSPaolo Bonzini     case WREN:
133049ab747fSPaolo Bonzini         s->write_enable = true;
133149ab747fSPaolo Bonzini         break;
133249ab747fSPaolo Bonzini 
133349ab747fSPaolo Bonzini     case RDSR:
133449ab747fSPaolo Bonzini         s->data[0] = (!!s->write_enable) << 1;
13352fa22a0fSIris Chen         s->data[0] |= (!!s->status_register_write_disabled) << 7;
13362113a128SIris Chen         s->data[0] |= (!!s->block_protect0) << 2;
13372113a128SIris Chen         s->data[0] |= (!!s->block_protect1) << 3;
13382113a128SIris Chen         s->data[0] |= (!!s->block_protect2) << 4;
13392113a128SIris Chen         if (s->pi->flags & HAS_SR_TB) {
13402113a128SIris Chen             s->data[0] |= (!!s->top_bottom_bit) << 5;
13412113a128SIris Chen         }
13422113a128SIris Chen         if (s->pi->flags & HAS_SR_BP3_BIT6) {
13432113a128SIris Chen             s->data[0] |= (!!s->block_protect3) << 6;
13442113a128SIris Chen         }
13452fa22a0fSIris Chen 
134610509e10SBin Meng         if (get_man(s) == MAN_MACRONIX || get_man(s) == MAN_ISSI) {
13477a69c100SMarcin Krzeminski             s->data[0] |= (!!s->quad_enable) << 6;
13487a69c100SMarcin Krzeminski         }
1349465ef47aSXuzhou Cheng         if (get_man(s) == MAN_SST) {
1350465ef47aSXuzhou Cheng             s->data[0] |= (!!s->aai_enable) << 6;
1351465ef47aSXuzhou Cheng         }
1352465ef47aSXuzhou Cheng 
135349ab747fSPaolo Bonzini         s->pos = 0;
135449ab747fSPaolo Bonzini         s->len = 1;
13550add925fSFrancisco Iglesias         s->data_read_loop = true;
135649ab747fSPaolo Bonzini         s->state = STATE_READING_DATA;
135749ab747fSPaolo Bonzini         break;
135849ab747fSPaolo Bonzini 
13599fbaa364SMarcin Krzeminski     case READ_FSR:
13609fbaa364SMarcin Krzeminski         s->data[0] = FSR_FLASH_READY;
13619fbaa364SMarcin Krzeminski         if (s->four_bytes_address_mode) {
13629fbaa364SMarcin Krzeminski             s->data[0] |= FSR_4BYTE_ADDR_MODE_ENABLED;
13639fbaa364SMarcin Krzeminski         }
13649fbaa364SMarcin Krzeminski         s->pos = 0;
13659fbaa364SMarcin Krzeminski         s->len = 1;
13660add925fSFrancisco Iglesias         s->data_read_loop = true;
13679fbaa364SMarcin Krzeminski         s->state = STATE_READING_DATA;
13689fbaa364SMarcin Krzeminski         break;
13699fbaa364SMarcin Krzeminski 
137049ab747fSPaolo Bonzini     case JEDEC_READ:
137123486231SJoe Komlodi         if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) == MODE_STD) {
1372ccc46090SGuenter Roeck             trace_m25p80_populated_jedec(s);
1373e3ba6cd6SMarcin Krzeminski             for (i = 0; i < s->pi->id_len; i++) {
1374e3ba6cd6SMarcin Krzeminski                 s->data[i] = s->pi->id[i];
137549ab747fSPaolo Bonzini             }
1376f3ee222fSGuenter Roeck             for (; i < SPI_NOR_MAX_ID_LEN; i++) {
1377f3ee222fSGuenter Roeck                 s->data[i] = 0;
1378f3ee222fSGuenter Roeck             }
1379e3ba6cd6SMarcin Krzeminski 
1380f3ee222fSGuenter Roeck             s->len = SPI_NOR_MAX_ID_LEN;
138149ab747fSPaolo Bonzini             s->pos = 0;
138249ab747fSPaolo Bonzini             s->state = STATE_READING_DATA;
138323486231SJoe Komlodi         } else {
138423486231SJoe Komlodi             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Cannot execute JEDEC read "
138523486231SJoe Komlodi                           "in DIO or QIO mode\n");
138623486231SJoe Komlodi         }
138749ab747fSPaolo Bonzini         break;
138849ab747fSPaolo Bonzini 
13897a69c100SMarcin Krzeminski     case RDCR:
13907a69c100SMarcin Krzeminski         s->data[0] = s->volatile_cfg & 0xFF;
13917a69c100SMarcin Krzeminski         s->data[0] |= (!!s->four_bytes_address_mode) << 5;
13927a69c100SMarcin Krzeminski         s->pos = 0;
13937a69c100SMarcin Krzeminski         s->len = 1;
13947a69c100SMarcin Krzeminski         s->state = STATE_READING_DATA;
13957a69c100SMarcin Krzeminski         break;
13967a69c100SMarcin Krzeminski 
13970f589782SFrancisco Iglesias     case BULK_ERASE_60:
139849ab747fSPaolo Bonzini     case BULK_ERASE:
139949ab747fSPaolo Bonzini         if (s->write_enable) {
1400ccc46090SGuenter Roeck             trace_m25p80_chip_erase(s);
140149ab747fSPaolo Bonzini             flash_erase(s, 0, BULK_ERASE);
140249ab747fSPaolo Bonzini         } else {
1403e9711b4dSPeter Crosthwaite             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: chip erase with write "
1404e9711b4dSPeter Crosthwaite                           "protect!\n");
140549ab747fSPaolo Bonzini         }
140649ab747fSPaolo Bonzini         break;
140749ab747fSPaolo Bonzini     case NOP:
140849ab747fSPaolo Bonzini         break;
1409c0f3f675SMarcin Krzeminski     case EN_4BYTE_ADDR:
1410c0f3f675SMarcin Krzeminski         s->four_bytes_address_mode = true;
1411c0f3f675SMarcin Krzeminski         break;
1412c0f3f675SMarcin Krzeminski     case EX_4BYTE_ADDR:
1413c0f3f675SMarcin Krzeminski         s->four_bytes_address_mode = false;
1414c0f3f675SMarcin Krzeminski         break;
14150f589782SFrancisco Iglesias     case BRRD:
1416d8a29a7aSMarcin Krzeminski     case EXTEND_ADDR_READ:
1417d8a29a7aSMarcin Krzeminski         s->data[0] = s->ear;
1418d8a29a7aSMarcin Krzeminski         s->pos = 0;
1419d8a29a7aSMarcin Krzeminski         s->len = 1;
1420d8a29a7aSMarcin Krzeminski         s->state = STATE_READING_DATA;
1421d8a29a7aSMarcin Krzeminski         break;
14220f589782SFrancisco Iglesias     case BRWR:
1423d8a29a7aSMarcin Krzeminski     case EXTEND_ADDR_WRITE:
1424d8a29a7aSMarcin Krzeminski         if (s->write_enable) {
1425d8a29a7aSMarcin Krzeminski             s->needed_bytes = 1;
1426d8a29a7aSMarcin Krzeminski             s->pos = 0;
1427d8a29a7aSMarcin Krzeminski             s->len = 0;
1428d8a29a7aSMarcin Krzeminski             s->state = STATE_COLLECTING_DATA;
1429d8a29a7aSMarcin Krzeminski         }
1430d8a29a7aSMarcin Krzeminski         break;
1431cb475951SMarcin Krzeminski     case RNVCR:
1432cb475951SMarcin Krzeminski         s->data[0] = s->nonvolatile_cfg & 0xFF;
1433cb475951SMarcin Krzeminski         s->data[1] = (s->nonvolatile_cfg >> 8) & 0xFF;
1434cb475951SMarcin Krzeminski         s->pos = 0;
1435cb475951SMarcin Krzeminski         s->len = 2;
1436cb475951SMarcin Krzeminski         s->state = STATE_READING_DATA;
1437cb475951SMarcin Krzeminski         break;
1438cb475951SMarcin Krzeminski     case WNVCR:
14397a69c100SMarcin Krzeminski         if (s->write_enable && get_man(s) == MAN_NUMONYX) {
1440cb475951SMarcin Krzeminski             s->needed_bytes = 2;
1441cb475951SMarcin Krzeminski             s->pos = 0;
1442cb475951SMarcin Krzeminski             s->len = 0;
1443cb475951SMarcin Krzeminski             s->state = STATE_COLLECTING_DATA;
1444cb475951SMarcin Krzeminski         }
1445cb475951SMarcin Krzeminski         break;
1446cb475951SMarcin Krzeminski     case RVCR:
1447cb475951SMarcin Krzeminski         s->data[0] = s->volatile_cfg & 0xFF;
1448cb475951SMarcin Krzeminski         s->pos = 0;
1449cb475951SMarcin Krzeminski         s->len = 1;
1450cb475951SMarcin Krzeminski         s->state = STATE_READING_DATA;
1451cb475951SMarcin Krzeminski         break;
1452cb475951SMarcin Krzeminski     case WVCR:
1453cb475951SMarcin Krzeminski         if (s->write_enable) {
1454cb475951SMarcin Krzeminski             s->needed_bytes = 1;
1455cb475951SMarcin Krzeminski             s->pos = 0;
1456cb475951SMarcin Krzeminski             s->len = 0;
1457cb475951SMarcin Krzeminski             s->state = STATE_COLLECTING_DATA;
1458cb475951SMarcin Krzeminski         }
1459cb475951SMarcin Krzeminski         break;
1460cb475951SMarcin Krzeminski     case REVCR:
1461cb475951SMarcin Krzeminski         s->data[0] = s->enh_volatile_cfg & 0xFF;
1462cb475951SMarcin Krzeminski         s->pos = 0;
1463cb475951SMarcin Krzeminski         s->len = 1;
1464cb475951SMarcin Krzeminski         s->state = STATE_READING_DATA;
1465cb475951SMarcin Krzeminski         break;
1466cb475951SMarcin Krzeminski     case WEVCR:
1467cb475951SMarcin Krzeminski         if (s->write_enable) {
1468cb475951SMarcin Krzeminski             s->needed_bytes = 1;
1469cb475951SMarcin Krzeminski             s->pos = 0;
1470cb475951SMarcin Krzeminski             s->len = 0;
1471cb475951SMarcin Krzeminski             s->state = STATE_COLLECTING_DATA;
1472cb475951SMarcin Krzeminski         }
1473cb475951SMarcin Krzeminski         break;
1474187c2636SMarcin Krzeminski     case RESET_ENABLE:
1475187c2636SMarcin Krzeminski         s->reset_enable = true;
1476187c2636SMarcin Krzeminski         break;
1477187c2636SMarcin Krzeminski     case RESET_MEMORY:
1478187c2636SMarcin Krzeminski         if (s->reset_enable) {
1479187c2636SMarcin Krzeminski             reset_memory(s);
1480187c2636SMarcin Krzeminski         }
1481187c2636SMarcin Krzeminski         break;
14827a69c100SMarcin Krzeminski     case RDCR_EQIO:
14837a69c100SMarcin Krzeminski         switch (get_man(s)) {
14847a69c100SMarcin Krzeminski         case MAN_SPANSION:
14857a69c100SMarcin Krzeminski             s->data[0] = (!!s->quad_enable) << 1;
14867a69c100SMarcin Krzeminski             s->pos = 0;
14877a69c100SMarcin Krzeminski             s->len = 1;
14887a69c100SMarcin Krzeminski             s->state = STATE_READING_DATA;
14897a69c100SMarcin Krzeminski             break;
14907a69c100SMarcin Krzeminski         case MAN_MACRONIX:
14917a69c100SMarcin Krzeminski             s->quad_enable = true;
14927a69c100SMarcin Krzeminski             break;
149361f93767SJamin Lin         case MAN_WINBOND:
149461f93767SJamin Lin             s->data[0] = (!!s->quad_enable) << 1;
149561f93767SJamin Lin             s->pos = 0;
149661f93767SJamin Lin             s->len = 1;
149761f93767SJamin Lin             s->state = STATE_READING_DATA;
149861f93767SJamin Lin             break;
14997a69c100SMarcin Krzeminski         default:
15007a69c100SMarcin Krzeminski             break;
15017a69c100SMarcin Krzeminski         }
15027a69c100SMarcin Krzeminski         break;
15037a69c100SMarcin Krzeminski     case RSTQIO:
15047a69c100SMarcin Krzeminski         s->quad_enable = false;
15057a69c100SMarcin Krzeminski         break;
1506465ef47aSXuzhou Cheng     case AAI_WP:
1507465ef47aSXuzhou Cheng         if (get_man(s) == MAN_SST) {
1508465ef47aSXuzhou Cheng             if (s->write_enable) {
1509465ef47aSXuzhou Cheng                 if (s->aai_enable) {
1510465ef47aSXuzhou Cheng                     s->state = STATE_PAGE_PROGRAM;
1511465ef47aSXuzhou Cheng                 } else {
1512465ef47aSXuzhou Cheng                     s->aai_enable = true;
1513465ef47aSXuzhou Cheng                     s->needed_bytes = get_addr_length(s);
1514465ef47aSXuzhou Cheng                     s->state = STATE_COLLECTING_DATA;
1515465ef47aSXuzhou Cheng                 }
1516465ef47aSXuzhou Cheng             } else {
1517465ef47aSXuzhou Cheng                 qemu_log_mask(LOG_GUEST_ERROR,
1518465ef47aSXuzhou Cheng                               "M25P80: AAI_WP with write protect\n");
1519465ef47aSXuzhou Cheng             }
1520465ef47aSXuzhou Cheng         } else {
1521465ef47aSXuzhou Cheng             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value);
1522465ef47aSXuzhou Cheng         }
1523465ef47aSXuzhou Cheng         break;
15242389bcc2SCédric Le Goater     case RDSFDP:
15252389bcc2SCédric Le Goater         if (s->pi->sfdp_read) {
15262389bcc2SCédric Le Goater             s->needed_bytes = get_addr_length(s) + 1; /* SFDP addr + dummy */
15272389bcc2SCédric Le Goater             s->pos = 0;
15282389bcc2SCédric Le Goater             s->len = 0;
15292389bcc2SCédric Le Goater             s->state = STATE_COLLECTING_DATA;
15302389bcc2SCédric Le Goater             break;
15312389bcc2SCédric Le Goater         }
15322389bcc2SCédric Le Goater         /* Fallthrough */
15332389bcc2SCédric Le Goater 
153449ab747fSPaolo Bonzini     default:
15359c85bcd8SGuenter Roeck         s->pos = 0;
15369c85bcd8SGuenter Roeck         s->len = 1;
15379c85bcd8SGuenter Roeck         s->state = STATE_READING_DATA;
15389c85bcd8SGuenter Roeck         s->data_read_loop = true;
15399c85bcd8SGuenter Roeck         s->data[0] = 0;
1540e9711b4dSPeter Crosthwaite         qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value);
154149ab747fSPaolo Bonzini         break;
154249ab747fSPaolo Bonzini     }
154349ab747fSPaolo Bonzini }
154449ab747fSPaolo Bonzini 
m25p80_cs(SSIPeripheral * ss,bool select)1545ec7e429bSPhilippe Mathieu-Daudé static int m25p80_cs(SSIPeripheral *ss, bool select)
154649ab747fSPaolo Bonzini {
1547cdccf7d7SPeter Crosthwaite     Flash *s = M25P80(ss);
154849ab747fSPaolo Bonzini 
154949ab747fSPaolo Bonzini     if (select) {
15509964674eSMarcin Krzeminski         if (s->state == STATE_COLLECTING_VAR_LEN_DATA) {
15519964674eSMarcin Krzeminski             complete_collecting_data(s);
15529964674eSMarcin Krzeminski         }
155349ab747fSPaolo Bonzini         s->len = 0;
155449ab747fSPaolo Bonzini         s->pos = 0;
155549ab747fSPaolo Bonzini         s->state = STATE_IDLE;
155649ab747fSPaolo Bonzini         flash_sync_dirty(s, -1);
15570add925fSFrancisco Iglesias         s->data_read_loop = false;
155849ab747fSPaolo Bonzini     }
155949ab747fSPaolo Bonzini 
1560ccc46090SGuenter Roeck     trace_m25p80_select(s, select ? "de" : "");
156149ab747fSPaolo Bonzini 
156249ab747fSPaolo Bonzini     return 0;
156349ab747fSPaolo Bonzini }
156449ab747fSPaolo Bonzini 
m25p80_transfer8(SSIPeripheral * ss,uint32_t tx)1565ec7e429bSPhilippe Mathieu-Daudé static uint32_t m25p80_transfer8(SSIPeripheral *ss, uint32_t tx)
156649ab747fSPaolo Bonzini {
1567cdccf7d7SPeter Crosthwaite     Flash *s = M25P80(ss);
156849ab747fSPaolo Bonzini     uint32_t r = 0;
156949ab747fSPaolo Bonzini 
1570ccc46090SGuenter Roeck     trace_m25p80_transfer(s, s->state, s->len, s->needed_bytes, s->pos,
1571ccc46090SGuenter Roeck                           s->cur_addr, (uint8_t)tx);
1572ccc46090SGuenter Roeck 
157349ab747fSPaolo Bonzini     switch (s->state) {
157449ab747fSPaolo Bonzini 
157549ab747fSPaolo Bonzini     case STATE_PAGE_PROGRAM:
1576ccc46090SGuenter Roeck         trace_m25p80_page_program(s, s->cur_addr, (uint8_t)tx);
157749ab747fSPaolo Bonzini         flash_write8(s, s->cur_addr, (uint8_t)tx);
1578b68cb060SPaolo Bonzini         s->cur_addr = (s->cur_addr + 1) & (s->size - 1);
1579465ef47aSXuzhou Cheng 
1580465ef47aSXuzhou Cheng         if (get_man(s) == MAN_SST && s->aai_enable && s->cur_addr == 0) {
1581465ef47aSXuzhou Cheng             /*
1582465ef47aSXuzhou Cheng              * There is no wrap mode during AAI programming once the highest
1583465ef47aSXuzhou Cheng              * unprotected memory address is reached. The Write-Enable-Latch
1584465ef47aSXuzhou Cheng              * bit is automatically reset, and AAI programming mode aborts.
1585465ef47aSXuzhou Cheng              */
1586465ef47aSXuzhou Cheng             s->write_enable = false;
1587465ef47aSXuzhou Cheng             s->aai_enable = false;
1588465ef47aSXuzhou Cheng         }
1589465ef47aSXuzhou Cheng 
159049ab747fSPaolo Bonzini         break;
159149ab747fSPaolo Bonzini 
159249ab747fSPaolo Bonzini     case STATE_READ:
159349ab747fSPaolo Bonzini         r = s->storage[s->cur_addr];
1594ccc46090SGuenter Roeck         trace_m25p80_read_byte(s, s->cur_addr, (uint8_t)r);
1595b68cb060SPaolo Bonzini         s->cur_addr = (s->cur_addr + 1) & (s->size - 1);
159649ab747fSPaolo Bonzini         break;
159749ab747fSPaolo Bonzini 
159849ab747fSPaolo Bonzini     case STATE_COLLECTING_DATA:
15999964674eSMarcin Krzeminski     case STATE_COLLECTING_VAR_LEN_DATA:
160024cb2e0dSJean-Christophe Dubois 
160124cb2e0dSJean-Christophe Dubois         if (s->len >= M25P80_INTERNAL_DATA_BUFFER_SZ) {
160224cb2e0dSJean-Christophe Dubois             qemu_log_mask(LOG_GUEST_ERROR,
160324cb2e0dSJean-Christophe Dubois                           "M25P80: Write overrun internal data buffer. "
160424cb2e0dSJean-Christophe Dubois                           "SPI controller (QEMU emulator or guest driver) "
160524cb2e0dSJean-Christophe Dubois                           "is misbehaving\n");
160624cb2e0dSJean-Christophe Dubois             s->len = s->pos = 0;
160724cb2e0dSJean-Christophe Dubois             s->state = STATE_IDLE;
160824cb2e0dSJean-Christophe Dubois             break;
160924cb2e0dSJean-Christophe Dubois         }
161024cb2e0dSJean-Christophe Dubois 
161149ab747fSPaolo Bonzini         s->data[s->len] = (uint8_t)tx;
161249ab747fSPaolo Bonzini         s->len++;
161349ab747fSPaolo Bonzini 
161449ab747fSPaolo Bonzini         if (s->len == s->needed_bytes) {
161549ab747fSPaolo Bonzini             complete_collecting_data(s);
161649ab747fSPaolo Bonzini         }
161749ab747fSPaolo Bonzini         break;
161849ab747fSPaolo Bonzini 
161949ab747fSPaolo Bonzini     case STATE_READING_DATA:
162024cb2e0dSJean-Christophe Dubois 
162124cb2e0dSJean-Christophe Dubois         if (s->pos >= M25P80_INTERNAL_DATA_BUFFER_SZ) {
162224cb2e0dSJean-Christophe Dubois             qemu_log_mask(LOG_GUEST_ERROR,
162324cb2e0dSJean-Christophe Dubois                           "M25P80: Read overrun internal data buffer. "
162424cb2e0dSJean-Christophe Dubois                           "SPI controller (QEMU emulator or guest driver) "
162524cb2e0dSJean-Christophe Dubois                           "is misbehaving\n");
162624cb2e0dSJean-Christophe Dubois             s->len = s->pos = 0;
162724cb2e0dSJean-Christophe Dubois             s->state = STATE_IDLE;
162824cb2e0dSJean-Christophe Dubois             break;
162924cb2e0dSJean-Christophe Dubois         }
163024cb2e0dSJean-Christophe Dubois 
163149ab747fSPaolo Bonzini         r = s->data[s->pos];
1632ccc46090SGuenter Roeck         trace_m25p80_read_data(s, s->pos, (uint8_t)r);
163349ab747fSPaolo Bonzini         s->pos++;
163449ab747fSPaolo Bonzini         if (s->pos == s->len) {
163549ab747fSPaolo Bonzini             s->pos = 0;
16360add925fSFrancisco Iglesias             if (!s->data_read_loop) {
163749ab747fSPaolo Bonzini                 s->state = STATE_IDLE;
163849ab747fSPaolo Bonzini             }
16390add925fSFrancisco Iglesias         }
164049ab747fSPaolo Bonzini         break;
16412389bcc2SCédric Le Goater     case STATE_READING_SFDP:
16422389bcc2SCédric Le Goater         assert(s->pi->sfdp_read);
16432389bcc2SCédric Le Goater         r = s->pi->sfdp_read(s->cur_addr);
16442389bcc2SCédric Le Goater         trace_m25p80_read_sfdp(s, s->cur_addr, (uint8_t)r);
16452389bcc2SCédric Le Goater         s->cur_addr = (s->cur_addr + 1) & (M25P80_SFDP_MAX_SIZE - 1);
16462389bcc2SCédric Le Goater         break;
164749ab747fSPaolo Bonzini 
164849ab747fSPaolo Bonzini     default:
164949ab747fSPaolo Bonzini     case STATE_IDLE:
165049ab747fSPaolo Bonzini         decode_new_cmd(s, (uint8_t)tx);
165149ab747fSPaolo Bonzini         break;
165249ab747fSPaolo Bonzini     }
165349ab747fSPaolo Bonzini 
165449ab747fSPaolo Bonzini     return r;
165549ab747fSPaolo Bonzini }
165649ab747fSPaolo Bonzini 
m25p80_write_protect_pin_irq_handler(void * opaque,int n,int level)16572fa22a0fSIris Chen static void m25p80_write_protect_pin_irq_handler(void *opaque, int n, int level)
16582fa22a0fSIris Chen {
16592fa22a0fSIris Chen     Flash *s = M25P80(opaque);
16602fa22a0fSIris Chen     /* WP# is just a single pin. */
16612fa22a0fSIris Chen     assert(n == 0);
16622fa22a0fSIris Chen     s->wp_level = !!level;
16632fa22a0fSIris Chen }
16642fa22a0fSIris Chen 
m25p80_realize(SSIPeripheral * ss,Error ** errp)1665ec7e429bSPhilippe Mathieu-Daudé static void m25p80_realize(SSIPeripheral *ss, Error **errp)
166649ab747fSPaolo Bonzini {
1667cdccf7d7SPeter Crosthwaite     Flash *s = M25P80(ss);
166849ab747fSPaolo Bonzini     M25P80Class *mc = M25P80_GET_CLASS(s);
1669a17c17a2SKevin Wolf     int ret;
167049ab747fSPaolo Bonzini 
167149ab747fSPaolo Bonzini     s->pi = mc->pi;
167249ab747fSPaolo Bonzini 
167349ab747fSPaolo Bonzini     s->size = s->pi->sector_size * s->pi->n_sectors;
167449ab747fSPaolo Bonzini     s->dirty_page = -1;
167549ab747fSPaolo Bonzini 
167673bce518SPaolo Bonzini     if (s->blk) {
1677a17c17a2SKevin Wolf         uint64_t perm = BLK_PERM_CONSISTENT_READ |
167886b1cf32SKevin Wolf                         (blk_supports_write_perm(s->blk) ? BLK_PERM_WRITE : 0);
1679a17c17a2SKevin Wolf         ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp);
1680a17c17a2SKevin Wolf         if (ret < 0) {
1681a17c17a2SKevin Wolf             return;
1682a17c17a2SKevin Wolf         }
1683a17c17a2SKevin Wolf 
1684ccc46090SGuenter Roeck         trace_m25p80_binding(s);
1685c485cf9cSStefan Hajnoczi         s->storage = blk_blockalign(s->blk, s->size);
1686c485cf9cSStefan Hajnoczi 
1687954b33daSManos Pitsidianakis         if (!blk_check_size_and_read_all(s->blk, DEVICE(s),
1688954b33daSManos Pitsidianakis                                          s->storage, s->size, errp)) {
16897673bb4cSCédric Le Goater             return;
169049ab747fSPaolo Bonzini         }
169149ab747fSPaolo Bonzini     } else {
1692ccc46090SGuenter Roeck         trace_m25p80_binding_no_bdrv(s);
1693c485cf9cSStefan Hajnoczi         s->storage = blk_blockalign(NULL, s->size);
169449ab747fSPaolo Bonzini         memset(s->storage, 0xFF, s->size);
169549ab747fSPaolo Bonzini     }
16962fa22a0fSIris Chen 
16972fa22a0fSIris Chen     qdev_init_gpio_in_named(DEVICE(s),
16982fa22a0fSIris Chen                             m25p80_write_protect_pin_irq_handler, "WP#", 1);
169949ab747fSPaolo Bonzini }
170049ab747fSPaolo Bonzini 
m25p80_reset(DeviceState * d)1701187c2636SMarcin Krzeminski static void m25p80_reset(DeviceState *d)
1702187c2636SMarcin Krzeminski {
1703187c2636SMarcin Krzeminski     Flash *s = M25P80(d);
1704187c2636SMarcin Krzeminski 
17052fa22a0fSIris Chen     s->wp_level = true;
17062fa22a0fSIris Chen     s->status_register_write_disabled = false;
17072113a128SIris Chen     s->block_protect0 = false;
17082113a128SIris Chen     s->block_protect1 = false;
17092113a128SIris Chen     s->block_protect2 = false;
17102113a128SIris Chen     s->block_protect3 = false;
17112113a128SIris Chen     s->top_bottom_bit = false;
17122fa22a0fSIris Chen 
1713187c2636SMarcin Krzeminski     reset_memory(s);
1714187c2636SMarcin Krzeminski }
1715187c2636SMarcin Krzeminski 
m25p80_pre_save(void * opaque)171644b1ff31SDr. David Alan Gilbert static int m25p80_pre_save(void *opaque)
171749ab747fSPaolo Bonzini {
171849ab747fSPaolo Bonzini     flash_sync_dirty((Flash *)opaque, -1);
171944b1ff31SDr. David Alan Gilbert 
172044b1ff31SDr. David Alan Gilbert     return 0;
172149ab747fSPaolo Bonzini }
172249ab747fSPaolo Bonzini 
1723cb475951SMarcin Krzeminski static Property m25p80_properties[] = {
1724d9cc8701SMarcin Krzeminski     /* This is default value for Micron flash */
1725188052a1SIris Chen     DEFINE_PROP_BOOL("write-enable", Flash, write_enable, false),
1726cb475951SMarcin Krzeminski     DEFINE_PROP_UINT32("nonvolatile-cfg", Flash, nonvolatile_cfg, 0x8FFF),
1727d9cc8701SMarcin Krzeminski     DEFINE_PROP_UINT8("spansion-cr1nv", Flash, spansion_cr1nv, 0x0),
1728d9cc8701SMarcin Krzeminski     DEFINE_PROP_UINT8("spansion-cr2nv", Flash, spansion_cr2nv, 0x8),
1729d9cc8701SMarcin Krzeminski     DEFINE_PROP_UINT8("spansion-cr3nv", Flash, spansion_cr3nv, 0x2),
1730d9cc8701SMarcin Krzeminski     DEFINE_PROP_UINT8("spansion-cr4nv", Flash, spansion_cr4nv, 0x10),
173173bce518SPaolo Bonzini     DEFINE_PROP_DRIVE("drive", Flash, blk),
1732cb475951SMarcin Krzeminski     DEFINE_PROP_END_OF_LIST(),
1733cb475951SMarcin Krzeminski };
1734cb475951SMarcin Krzeminski 
m25p80_pre_load(void * opaque)17350add925fSFrancisco Iglesias static int m25p80_pre_load(void *opaque)
17360add925fSFrancisco Iglesias {
17370add925fSFrancisco Iglesias     Flash *s = (Flash *)opaque;
17380add925fSFrancisco Iglesias 
17390add925fSFrancisco Iglesias     s->data_read_loop = false;
17400add925fSFrancisco Iglesias     return 0;
17410add925fSFrancisco Iglesias }
17420add925fSFrancisco Iglesias 
m25p80_data_read_loop_needed(void * opaque)17430add925fSFrancisco Iglesias static bool m25p80_data_read_loop_needed(void *opaque)
17440add925fSFrancisco Iglesias {
17450add925fSFrancisco Iglesias     Flash *s = (Flash *)opaque;
17460add925fSFrancisco Iglesias 
17470add925fSFrancisco Iglesias     return s->data_read_loop;
17480add925fSFrancisco Iglesias }
17490add925fSFrancisco Iglesias 
17500add925fSFrancisco Iglesias static const VMStateDescription vmstate_m25p80_data_read_loop = {
17510add925fSFrancisco Iglesias     .name = "m25p80/data_read_loop",
17520add925fSFrancisco Iglesias     .version_id = 1,
17530add925fSFrancisco Iglesias     .minimum_version_id = 1,
17540add925fSFrancisco Iglesias     .needed = m25p80_data_read_loop_needed,
17557d5dc0a3SRichard Henderson     .fields = (const VMStateField[]) {
17560add925fSFrancisco Iglesias         VMSTATE_BOOL(data_read_loop, Flash),
17570add925fSFrancisco Iglesias         VMSTATE_END_OF_LIST()
17580add925fSFrancisco Iglesias     }
17590add925fSFrancisco Iglesias };
17600add925fSFrancisco Iglesias 
m25p80_aai_enable_needed(void * opaque)1761465ef47aSXuzhou Cheng static bool m25p80_aai_enable_needed(void *opaque)
1762465ef47aSXuzhou Cheng {
1763465ef47aSXuzhou Cheng     Flash *s = (Flash *)opaque;
1764465ef47aSXuzhou Cheng 
1765465ef47aSXuzhou Cheng     return s->aai_enable;
1766465ef47aSXuzhou Cheng }
1767465ef47aSXuzhou Cheng 
1768465ef47aSXuzhou Cheng static const VMStateDescription vmstate_m25p80_aai_enable = {
1769465ef47aSXuzhou Cheng     .name = "m25p80/aai_enable",
1770465ef47aSXuzhou Cheng     .version_id = 1,
1771465ef47aSXuzhou Cheng     .minimum_version_id = 1,
1772465ef47aSXuzhou Cheng     .needed = m25p80_aai_enable_needed,
17737d5dc0a3SRichard Henderson     .fields = (const VMStateField[]) {
1774465ef47aSXuzhou Cheng         VMSTATE_BOOL(aai_enable, Flash),
1775465ef47aSXuzhou Cheng         VMSTATE_END_OF_LIST()
1776465ef47aSXuzhou Cheng     }
1777465ef47aSXuzhou Cheng };
1778465ef47aSXuzhou Cheng 
m25p80_wp_level_srwd_needed(void * opaque)17792fa22a0fSIris Chen static bool m25p80_wp_level_srwd_needed(void *opaque)
17802fa22a0fSIris Chen {
17812fa22a0fSIris Chen     Flash *s = (Flash *)opaque;
17822fa22a0fSIris Chen 
17832fa22a0fSIris Chen     return !s->wp_level || s->status_register_write_disabled;
17842fa22a0fSIris Chen }
17852fa22a0fSIris Chen 
17862fa22a0fSIris Chen static const VMStateDescription vmstate_m25p80_write_protect = {
17872fa22a0fSIris Chen     .name = "m25p80/write_protect",
17882fa22a0fSIris Chen     .version_id = 1,
17892fa22a0fSIris Chen     .minimum_version_id = 1,
17902fa22a0fSIris Chen     .needed = m25p80_wp_level_srwd_needed,
17917d5dc0a3SRichard Henderson     .fields = (const VMStateField[]) {
17922fa22a0fSIris Chen         VMSTATE_BOOL(wp_level, Flash),
17932fa22a0fSIris Chen         VMSTATE_BOOL(status_register_write_disabled, Flash),
17942fa22a0fSIris Chen         VMSTATE_END_OF_LIST()
17952fa22a0fSIris Chen     }
17962fa22a0fSIris Chen };
17972fa22a0fSIris Chen 
m25p80_block_protect_needed(void * opaque)17982113a128SIris Chen static bool m25p80_block_protect_needed(void *opaque)
17992113a128SIris Chen {
18002113a128SIris Chen     Flash *s = (Flash *)opaque;
18012113a128SIris Chen 
18022113a128SIris Chen     return s->block_protect0 ||
18032113a128SIris Chen            s->block_protect1 ||
18042113a128SIris Chen            s->block_protect2 ||
18052113a128SIris Chen            s->block_protect3 ||
18062113a128SIris Chen            s->top_bottom_bit;
18072113a128SIris Chen }
18082113a128SIris Chen 
18092113a128SIris Chen static const VMStateDescription vmstate_m25p80_block_protect = {
18102113a128SIris Chen     .name = "m25p80/block_protect",
18112113a128SIris Chen     .version_id = 1,
18122113a128SIris Chen     .minimum_version_id = 1,
18132113a128SIris Chen     .needed = m25p80_block_protect_needed,
18147d5dc0a3SRichard Henderson     .fields = (const VMStateField[]) {
18152113a128SIris Chen         VMSTATE_BOOL(block_protect0, Flash),
18162113a128SIris Chen         VMSTATE_BOOL(block_protect1, Flash),
18172113a128SIris Chen         VMSTATE_BOOL(block_protect2, Flash),
18182113a128SIris Chen         VMSTATE_BOOL(block_protect3, Flash),
18192113a128SIris Chen         VMSTATE_BOOL(top_bottom_bit, Flash),
18202113a128SIris Chen         VMSTATE_END_OF_LIST()
18212113a128SIris Chen     }
18222113a128SIris Chen };
18232113a128SIris Chen 
182449ab747fSPaolo Bonzini static const VMStateDescription vmstate_m25p80 = {
1825c827c06aSMarcin Krzeminski     .name = "m25p80",
1826c827c06aSMarcin Krzeminski     .version_id = 0,
1827c827c06aSMarcin Krzeminski     .minimum_version_id = 0,
182849ab747fSPaolo Bonzini     .pre_save = m25p80_pre_save,
18290add925fSFrancisco Iglesias     .pre_load = m25p80_pre_load,
18307d5dc0a3SRichard Henderson     .fields = (const VMStateField[]) {
183149ab747fSPaolo Bonzini         VMSTATE_UINT8(state, Flash),
183224cb2e0dSJean-Christophe Dubois         VMSTATE_UINT8_ARRAY(data, Flash, M25P80_INTERNAL_DATA_BUFFER_SZ),
183349ab747fSPaolo Bonzini         VMSTATE_UINT32(len, Flash),
183449ab747fSPaolo Bonzini         VMSTATE_UINT32(pos, Flash),
183549ab747fSPaolo Bonzini         VMSTATE_UINT8(needed_bytes, Flash),
183649ab747fSPaolo Bonzini         VMSTATE_UINT8(cmd_in_progress, Flash),
1837b7f480c3SPaolo Bonzini         VMSTATE_UINT32(cur_addr, Flash),
183849ab747fSPaolo Bonzini         VMSTATE_BOOL(write_enable, Flash),
1839c827c06aSMarcin Krzeminski         VMSTATE_BOOL(reset_enable, Flash),
1840c827c06aSMarcin Krzeminski         VMSTATE_UINT8(ear, Flash),
1841c827c06aSMarcin Krzeminski         VMSTATE_BOOL(four_bytes_address_mode, Flash),
1842c827c06aSMarcin Krzeminski         VMSTATE_UINT32(nonvolatile_cfg, Flash),
1843c827c06aSMarcin Krzeminski         VMSTATE_UINT32(volatile_cfg, Flash),
1844c827c06aSMarcin Krzeminski         VMSTATE_UINT32(enh_volatile_cfg, Flash),
1845c827c06aSMarcin Krzeminski         VMSTATE_BOOL(quad_enable, Flash),
1846c827c06aSMarcin Krzeminski         VMSTATE_UINT8(spansion_cr1nv, Flash),
1847c827c06aSMarcin Krzeminski         VMSTATE_UINT8(spansion_cr2nv, Flash),
1848c827c06aSMarcin Krzeminski         VMSTATE_UINT8(spansion_cr3nv, Flash),
1849c827c06aSMarcin Krzeminski         VMSTATE_UINT8(spansion_cr4nv, Flash),
185049ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
18510add925fSFrancisco Iglesias     },
18527d5dc0a3SRichard Henderson     .subsections = (const VMStateDescription * const []) {
18530add925fSFrancisco Iglesias         &vmstate_m25p80_data_read_loop,
1854465ef47aSXuzhou Cheng         &vmstate_m25p80_aai_enable,
18552fa22a0fSIris Chen         &vmstate_m25p80_write_protect,
18562113a128SIris Chen         &vmstate_m25p80_block_protect,
18570add925fSFrancisco Iglesias         NULL
185849ab747fSPaolo Bonzini     }
185949ab747fSPaolo Bonzini };
186049ab747fSPaolo Bonzini 
m25p80_class_init(ObjectClass * klass,void * data)186149ab747fSPaolo Bonzini static void m25p80_class_init(ObjectClass *klass, void *data)
186249ab747fSPaolo Bonzini {
186349ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
1864ec7e429bSPhilippe Mathieu-Daudé     SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
186549ab747fSPaolo Bonzini     M25P80Class *mc = M25P80_CLASS(klass);
186649ab747fSPaolo Bonzini 
18677673bb4cSCédric Le Goater     k->realize = m25p80_realize;
186849ab747fSPaolo Bonzini     k->transfer = m25p80_transfer8;
186949ab747fSPaolo Bonzini     k->set_cs = m25p80_cs;
187049ab747fSPaolo Bonzini     k->cs_polarity = SSI_CS_LOW;
187149ab747fSPaolo Bonzini     dc->vmsd = &vmstate_m25p80;
18724f67d30bSMarc-André Lureau     device_class_set_props(dc, m25p80_properties);
1873e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, m25p80_reset);
187449ab747fSPaolo Bonzini     mc->pi = data;
187549ab747fSPaolo Bonzini }
187649ab747fSPaolo Bonzini 
187749ab747fSPaolo Bonzini static const TypeInfo m25p80_info = {
187849ab747fSPaolo Bonzini     .name           = TYPE_M25P80,
1879ec7e429bSPhilippe Mathieu-Daudé     .parent         = TYPE_SSI_PERIPHERAL,
188049ab747fSPaolo Bonzini     .instance_size  = sizeof(Flash),
188149ab747fSPaolo Bonzini     .class_size     = sizeof(M25P80Class),
188249ab747fSPaolo Bonzini     .abstract       = true,
188349ab747fSPaolo Bonzini };
188449ab747fSPaolo Bonzini 
m25p80_register_types(void)188549ab747fSPaolo Bonzini static void m25p80_register_types(void)
188649ab747fSPaolo Bonzini {
188749ab747fSPaolo Bonzini     int i;
188849ab747fSPaolo Bonzini 
188949ab747fSPaolo Bonzini     type_register_static(&m25p80_info);
189049ab747fSPaolo Bonzini     for (i = 0; i < ARRAY_SIZE(known_devices); ++i) {
1891c0400e3aSJamin Lin         const TypeInfo ti = {
189249ab747fSPaolo Bonzini             .name       = known_devices[i].part_name,
189349ab747fSPaolo Bonzini             .parent     = TYPE_M25P80,
189449ab747fSPaolo Bonzini             .class_init = m25p80_class_init,
189549ab747fSPaolo Bonzini             .class_data = (void *)&known_devices[i],
189649ab747fSPaolo Bonzini         };
189749ab747fSPaolo Bonzini         type_register(&ti);
189849ab747fSPaolo Bonzini     }
189949ab747fSPaolo Bonzini }
190049ab747fSPaolo Bonzini 
type_init(m25p80_register_types)190149ab747fSPaolo Bonzini type_init(m25p80_register_types)
19029ab26b0eSCédric Le Goater 
19039ab26b0eSCédric Le Goater BlockBackend *m25p80_get_blk(DeviceState *dev)
19049ab26b0eSCédric Le Goater {
19059ab26b0eSCédric Le Goater     return M25P80(dev)->blk;
19069ab26b0eSCédric Le Goater }
1907