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