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