xref: /qemu/hw/block/pflash_cfi02.c (revision 954b33da)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  *  CFI parallel flash with AMD command set emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  *  Copyright (c) 2005 Jocelyn Mayer
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * This library is free software; you can redistribute it and/or
749ab747fSPaolo Bonzini  * modify it under the terms of the GNU Lesser General Public
849ab747fSPaolo Bonzini  * License as published by the Free Software Foundation; either
93564a919SChetan Pant  * version 2.1 of the License, or (at your option) any later version.
1049ab747fSPaolo Bonzini  *
1149ab747fSPaolo Bonzini  * This library is distributed in the hope that it will be useful,
1249ab747fSPaolo Bonzini  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1349ab747fSPaolo Bonzini  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1449ab747fSPaolo Bonzini  * Lesser General Public License for more details.
1549ab747fSPaolo Bonzini  *
1649ab747fSPaolo Bonzini  * You should have received a copy of the GNU Lesser General Public
1749ab747fSPaolo Bonzini  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
1849ab747fSPaolo Bonzini  */
1949ab747fSPaolo Bonzini 
2049ab747fSPaolo Bonzini /*
2149ab747fSPaolo Bonzini  * For now, this code can emulate flashes of 1, 2 or 4 bytes width.
2249ab747fSPaolo Bonzini  * Supported commands/modes are:
2349ab747fSPaolo Bonzini  * - flash read
2449ab747fSPaolo Bonzini  * - flash write
2549ab747fSPaolo Bonzini  * - flash ID read
2649ab747fSPaolo Bonzini  * - sector erase
2749ab747fSPaolo Bonzini  * - chip erase
2849ab747fSPaolo Bonzini  * - unlock bypass command
2949ab747fSPaolo Bonzini  * - CFI queries
3049ab747fSPaolo Bonzini  *
3149ab747fSPaolo Bonzini  * It does not support flash interleaving.
3249ab747fSPaolo Bonzini  * It does not implement software data protection as found in many real chips
3349ab747fSPaolo Bonzini  */
3449ab747fSPaolo Bonzini 
3580c71a24SPeter Maydell #include "qemu/osdep.h"
3606f15217SMarkus Armbruster #include "hw/block/block.h"
3749ab747fSPaolo Bonzini #include "hw/block/flash.h"
38a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
39ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
40da34e65cSMarkus Armbruster #include "qapi/error.h"
411857b9dbSMansour Ahmadi #include "qemu/error-report.h"
42ddb6f225SStephen Checkoway #include "qemu/bitmap.h"
4349ab747fSPaolo Bonzini #include "qemu/timer.h"
444be74634SMarkus Armbruster #include "sysemu/block-backend.h"
4549ab747fSPaolo Bonzini #include "qemu/host-utils.h"
460b8fa32fSMarkus Armbruster #include "qemu/module.h"
4749ab747fSPaolo Bonzini #include "hw/sysbus.h"
48d6454270SMarkus Armbruster #include "migration/vmstate.h"
4913019f1fSPhilippe Mathieu-Daudé #include "trace.h"
5049ab747fSPaolo Bonzini 
5149ab747fSPaolo Bonzini #define PFLASH_LAZY_ROMD_THRESHOLD 42
5249ab747fSPaolo Bonzini 
5364659053SStephen Checkoway /*
5464659053SStephen Checkoway  * The size of the cfi_table indirectly depends on this and the start of the
5564659053SStephen Checkoway  * PRI table directly depends on it. 4 is the maximum size (and also what
5664659053SStephen Checkoway  * seems common) without changing the PRT table address.
5764659053SStephen Checkoway  */
5864659053SStephen Checkoway #define PFLASH_MAX_ERASE_REGIONS 4
5964659053SStephen Checkoway 
60aeaf6c20SPhilippe Mathieu-Daudé /* Special write cycles for CFI queries. */
61aeaf6c20SPhilippe Mathieu-Daudé enum {
62aeaf6c20SPhilippe Mathieu-Daudé     WCYCLE_CFI              = 7,
6346fb7809SStephen Checkoway     WCYCLE_AUTOSELECT_CFI   = 8,
64aeaf6c20SPhilippe Mathieu-Daudé };
65aeaf6c20SPhilippe Mathieu-Daudé 
6616434065SMarkus Armbruster struct PFlashCFI02 {
673509c396SHu Tao     /*< private >*/
683509c396SHu Tao     SysBusDevice parent_obj;
693509c396SHu Tao     /*< public >*/
703509c396SHu Tao 
714be74634SMarkus Armbruster     BlockBackend *blk;
7264659053SStephen Checkoway     uint32_t uniform_nb_blocs;
7364659053SStephen Checkoway     uint32_t uniform_sector_len;
74ddb6f225SStephen Checkoway     uint32_t total_sectors;
7564659053SStephen Checkoway     uint32_t nb_blocs[PFLASH_MAX_ERASE_REGIONS];
7664659053SStephen Checkoway     uint32_t sector_len[PFLASH_MAX_ERASE_REGIONS];
7749ab747fSPaolo Bonzini     uint32_t chip_len;
7849ab747fSPaolo Bonzini     uint8_t mappings;
7949ab747fSPaolo Bonzini     uint8_t width;
8049ab747fSPaolo Bonzini     uint8_t be;
8149ab747fSPaolo Bonzini     int wcycle; /* if 0, the flash is read normally */
8249ab747fSPaolo Bonzini     int bypass;
8349ab747fSPaolo Bonzini     int ro;
8449ab747fSPaolo Bonzini     uint8_t cmd;
8549ab747fSPaolo Bonzini     uint8_t status;
8649ab747fSPaolo Bonzini     /* FIXME: implement array device properties */
8749ab747fSPaolo Bonzini     uint16_t ident0;
8849ab747fSPaolo Bonzini     uint16_t ident1;
8949ab747fSPaolo Bonzini     uint16_t ident2;
9049ab747fSPaolo Bonzini     uint16_t ident3;
9149ab747fSPaolo Bonzini     uint16_t unlock_addr0;
9249ab747fSPaolo Bonzini     uint16_t unlock_addr1;
9364659053SStephen Checkoway     uint8_t cfi_table[0x4d];
94d80cf1ebSStephen Checkoway     QEMUTimer timer;
95ccd8014bSPhilippe Mathieu-Daudé     /*
96ccd8014bSPhilippe Mathieu-Daudé      * The device replicates the flash memory across its memory space.  Emulate
9749ab747fSPaolo Bonzini      * that by having a container (.mem) filled with an array of aliases
9849ab747fSPaolo Bonzini      * (.mem_mappings) pointing to the flash memory (.orig_mem).
9949ab747fSPaolo Bonzini      */
10049ab747fSPaolo Bonzini     MemoryRegion mem;
10149ab747fSPaolo Bonzini     MemoryRegion *mem_mappings;    /* array; one per mapping */
10249ab747fSPaolo Bonzini     MemoryRegion orig_mem;
103326d02c3SPhilippe Mathieu-Daudé     bool rom_mode;
10449ab747fSPaolo Bonzini     int read_counter; /* used for lazy switch-back to rom mode */
105a50547acSStephen Checkoway     int sectors_to_erase;
106ddb6f225SStephen Checkoway     uint64_t erase_time_remaining;
107ddb6f225SStephen Checkoway     unsigned long *sector_erase_map;
10849ab747fSPaolo Bonzini     char *name;
10949ab747fSPaolo Bonzini     void *storage;
11049ab747fSPaolo Bonzini };
11149ab747fSPaolo Bonzini 
11249ab747fSPaolo Bonzini /*
1131d311e73SPhilippe Mathieu-Daudé  * Toggle status bit DQ7.
1141d311e73SPhilippe Mathieu-Daudé  */
toggle_dq7(PFlashCFI02 * pfl)1151d311e73SPhilippe Mathieu-Daudé static inline void toggle_dq7(PFlashCFI02 *pfl)
1161d311e73SPhilippe Mathieu-Daudé {
1171d311e73SPhilippe Mathieu-Daudé     pfl->status ^= 0x80;
1181d311e73SPhilippe Mathieu-Daudé }
1191d311e73SPhilippe Mathieu-Daudé 
1201d311e73SPhilippe Mathieu-Daudé /*
1211d311e73SPhilippe Mathieu-Daudé  * Set status bit DQ7 to bit 7 of value.
1221d311e73SPhilippe Mathieu-Daudé  */
set_dq7(PFlashCFI02 * pfl,uint8_t value)1231d311e73SPhilippe Mathieu-Daudé static inline void set_dq7(PFlashCFI02 *pfl, uint8_t value)
1241d311e73SPhilippe Mathieu-Daudé {
1251d311e73SPhilippe Mathieu-Daudé     pfl->status &= 0x7F;
1261d311e73SPhilippe Mathieu-Daudé     pfl->status |= value & 0x80;
1271d311e73SPhilippe Mathieu-Daudé }
1281d311e73SPhilippe Mathieu-Daudé 
1291d311e73SPhilippe Mathieu-Daudé /*
1301d311e73SPhilippe Mathieu-Daudé  * Toggle status bit DQ6.
1311d311e73SPhilippe Mathieu-Daudé  */
toggle_dq6(PFlashCFI02 * pfl)1321d311e73SPhilippe Mathieu-Daudé static inline void toggle_dq6(PFlashCFI02 *pfl)
1331d311e73SPhilippe Mathieu-Daudé {
1341d311e73SPhilippe Mathieu-Daudé     pfl->status ^= 0x40;
1351d311e73SPhilippe Mathieu-Daudé }
1361d311e73SPhilippe Mathieu-Daudé 
1371d311e73SPhilippe Mathieu-Daudé /*
138a50547acSStephen Checkoway  * Turn on DQ3.
139a50547acSStephen Checkoway  */
assert_dq3(PFlashCFI02 * pfl)140a50547acSStephen Checkoway static inline void assert_dq3(PFlashCFI02 *pfl)
141a50547acSStephen Checkoway {
142a50547acSStephen Checkoway     pfl->status |= 0x08;
143a50547acSStephen Checkoway }
144a50547acSStephen Checkoway 
145a50547acSStephen Checkoway /*
146a50547acSStephen Checkoway  * Turn off DQ3.
147a50547acSStephen Checkoway  */
reset_dq3(PFlashCFI02 * pfl)148a50547acSStephen Checkoway static inline void reset_dq3(PFlashCFI02 *pfl)
149a50547acSStephen Checkoway {
150a50547acSStephen Checkoway     pfl->status &= ~0x08;
151a50547acSStephen Checkoway }
152a50547acSStephen Checkoway 
153a50547acSStephen Checkoway /*
154ddb6f225SStephen Checkoway  * Toggle status bit DQ2.
155ddb6f225SStephen Checkoway  */
toggle_dq2(PFlashCFI02 * pfl)156ddb6f225SStephen Checkoway static inline void toggle_dq2(PFlashCFI02 *pfl)
157ddb6f225SStephen Checkoway {
158ddb6f225SStephen Checkoway     pfl->status ^= 0x04;
159ddb6f225SStephen Checkoway }
160ddb6f225SStephen Checkoway 
161ddb6f225SStephen Checkoway /*
16249ab747fSPaolo Bonzini  * Set up replicated mappings of the same region.
16349ab747fSPaolo Bonzini  */
pflash_setup_mappings(PFlashCFI02 * pfl)16416434065SMarkus Armbruster static void pflash_setup_mappings(PFlashCFI02 *pfl)
16549ab747fSPaolo Bonzini {
16649ab747fSPaolo Bonzini     unsigned i;
16749ab747fSPaolo Bonzini     hwaddr size = memory_region_size(&pfl->orig_mem);
16849ab747fSPaolo Bonzini 
1692d256e6fSPaolo Bonzini     memory_region_init(&pfl->mem, OBJECT(pfl), "pflash", pfl->mappings * size);
17049ab747fSPaolo Bonzini     pfl->mem_mappings = g_new(MemoryRegion, pfl->mappings);
17149ab747fSPaolo Bonzini     for (i = 0; i < pfl->mappings; ++i) {
1722d256e6fSPaolo Bonzini         memory_region_init_alias(&pfl->mem_mappings[i], OBJECT(pfl),
1732d256e6fSPaolo Bonzini                                  "pflash-alias", &pfl->orig_mem, 0, size);
17449ab747fSPaolo Bonzini         memory_region_add_subregion(&pfl->mem, i * size, &pfl->mem_mappings[i]);
17549ab747fSPaolo Bonzini     }
17649ab747fSPaolo Bonzini }
17749ab747fSPaolo Bonzini 
pflash_reset_state_machine(PFlashCFI02 * pfl)1787d1df53fSPhilippe Mathieu-Daudé static void pflash_reset_state_machine(PFlashCFI02 *pfl)
1797d1df53fSPhilippe Mathieu-Daudé {
18091316cbbSDavid Edmondson     trace_pflash_reset(pfl->name);
1817d1df53fSPhilippe Mathieu-Daudé     pfl->cmd = 0x00;
1827d1df53fSPhilippe Mathieu-Daudé     pfl->wcycle = 0;
1837d1df53fSPhilippe Mathieu-Daudé }
1847d1df53fSPhilippe Mathieu-Daudé 
pflash_mode_read_array(PFlashCFI02 * pfl)1857cb10960SPhilippe Mathieu-Daudé static void pflash_mode_read_array(PFlashCFI02 *pfl)
18649ab747fSPaolo Bonzini {
18791316cbbSDavid Edmondson     trace_pflash_mode_read_array(pfl->name);
1887d1df53fSPhilippe Mathieu-Daudé     pflash_reset_state_machine(pfl);
1897cb10960SPhilippe Mathieu-Daudé     pfl->rom_mode = true;
1907cb10960SPhilippe Mathieu-Daudé     memory_region_rom_device_set_romd(&pfl->orig_mem, true);
19149ab747fSPaolo Bonzini }
19249ab747fSPaolo Bonzini 
pflash_regions_count(PFlashCFI02 * pfl)193102f0f79SPhilippe Mathieu-Daudé static size_t pflash_regions_count(PFlashCFI02 *pfl)
194102f0f79SPhilippe Mathieu-Daudé {
195102f0f79SPhilippe Mathieu-Daudé     return pfl->cfi_table[0x2c];
196102f0f79SPhilippe Mathieu-Daudé }
197102f0f79SPhilippe Mathieu-Daudé 
198ddb6f225SStephen Checkoway /*
199ddb6f225SStephen Checkoway  * Returns the time it takes to erase the number of sectors scheduled for
200ddb6f225SStephen Checkoway  * erasure based on CFI address 0x21 which is "Typical timeout per individual
201ddb6f225SStephen Checkoway  * block erase 2^N ms."
202ddb6f225SStephen Checkoway  */
pflash_erase_time(PFlashCFI02 * pfl)203ddb6f225SStephen Checkoway static uint64_t pflash_erase_time(PFlashCFI02 *pfl)
204ddb6f225SStephen Checkoway {
205ddb6f225SStephen Checkoway     /*
206ddb6f225SStephen Checkoway      * If there are no sectors to erase (which can happen if all of the sectors
207ddb6f225SStephen Checkoway      * to be erased are protected), then erase takes 100 us. Protected sectors
208ddb6f225SStephen Checkoway      * aren't supported so this should never happen.
209ddb6f225SStephen Checkoway      */
210ddb6f225SStephen Checkoway     return ((1ULL << pfl->cfi_table[0x21]) * pfl->sectors_to_erase) * SCALE_US;
211ddb6f225SStephen Checkoway }
212ddb6f225SStephen Checkoway 
213ddb6f225SStephen Checkoway /*
214ddb6f225SStephen Checkoway  * Returns true if the device is currently in erase suspend mode.
215ddb6f225SStephen Checkoway  */
pflash_erase_suspend_mode(PFlashCFI02 * pfl)216ddb6f225SStephen Checkoway static inline bool pflash_erase_suspend_mode(PFlashCFI02 *pfl)
217ddb6f225SStephen Checkoway {
218ddb6f225SStephen Checkoway     return pfl->erase_time_remaining > 0;
219ddb6f225SStephen Checkoway }
220ddb6f225SStephen Checkoway 
pflash_timer(void * opaque)22149ab747fSPaolo Bonzini static void pflash_timer(void *opaque)
22249ab747fSPaolo Bonzini {
22316434065SMarkus Armbruster     PFlashCFI02 *pfl = opaque;
22449ab747fSPaolo Bonzini 
22591316cbbSDavid Edmondson     trace_pflash_timer_expired(pfl->name, pfl->cmd);
226a50547acSStephen Checkoway     if (pfl->cmd == 0x30) {
227a50547acSStephen Checkoway         /*
228a50547acSStephen Checkoway          * Sector erase. If DQ3 is 0 when the timer expires, then the 50
229a50547acSStephen Checkoway          * us erase timeout has expired so we need to start the timer for the
230a50547acSStephen Checkoway          * sector erase algorithm. Otherwise, the erase completed and we should
231a50547acSStephen Checkoway          * go back to read array mode.
232a50547acSStephen Checkoway          */
233a50547acSStephen Checkoway         if ((pfl->status & 0x08) == 0) {
234a50547acSStephen Checkoway             assert_dq3(pfl);
235ddb6f225SStephen Checkoway             uint64_t timeout = pflash_erase_time(pfl);
236a50547acSStephen Checkoway             timer_mod(&pfl->timer,
237a50547acSStephen Checkoway                       qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + timeout);
23891316cbbSDavid Edmondson             trace_pflash_erase_timeout(pfl->name, pfl->sectors_to_erase);
239a50547acSStephen Checkoway             return;
240a50547acSStephen Checkoway         }
24191316cbbSDavid Edmondson         trace_pflash_erase_complete(pfl->name);
242ddb6f225SStephen Checkoway         bitmap_zero(pfl->sector_erase_map, pfl->total_sectors);
243a50547acSStephen Checkoway         pfl->sectors_to_erase = 0;
244a50547acSStephen Checkoway         reset_dq3(pfl);
245a50547acSStephen Checkoway     }
246a50547acSStephen Checkoway 
24749ab747fSPaolo Bonzini     /* Reset flash */
2481d311e73SPhilippe Mathieu-Daudé     toggle_dq7(pfl);
24949ab747fSPaolo Bonzini     if (pfl->bypass) {
25049ab747fSPaolo Bonzini         pfl->wcycle = 2;
25149ab747fSPaolo Bonzini         pfl->cmd = 0;
2527cb10960SPhilippe Mathieu-Daudé     } else {
2537cb10960SPhilippe Mathieu-Daudé         pflash_mode_read_array(pfl);
2547cb10960SPhilippe Mathieu-Daudé     }
25549ab747fSPaolo Bonzini }
25649ab747fSPaolo Bonzini 
25706e8b8e3SPhilippe Mathieu-Daudé /*
25806e8b8e3SPhilippe Mathieu-Daudé  * Read data from flash.
25906e8b8e3SPhilippe Mathieu-Daudé  */
pflash_data_read(PFlashCFI02 * pfl,hwaddr offset,unsigned int width)26006e8b8e3SPhilippe Mathieu-Daudé static uint64_t pflash_data_read(PFlashCFI02 *pfl, hwaddr offset,
26106e8b8e3SPhilippe Mathieu-Daudé                                  unsigned int width)
26206e8b8e3SPhilippe Mathieu-Daudé {
26306e8b8e3SPhilippe Mathieu-Daudé     uint8_t *p = (uint8_t *)pfl->storage + offset;
26406e8b8e3SPhilippe Mathieu-Daudé     uint64_t ret = pfl->be ? ldn_be_p(p, width) : ldn_le_p(p, width);
26591316cbbSDavid Edmondson     trace_pflash_data_read(pfl->name, offset, width, ret);
26606e8b8e3SPhilippe Mathieu-Daudé     return ret;
26706e8b8e3SPhilippe Mathieu-Daudé }
26806e8b8e3SPhilippe Mathieu-Daudé 
269ddb6f225SStephen Checkoway typedef struct {
270ddb6f225SStephen Checkoway     uint32_t len;
271ddb6f225SStephen Checkoway     uint32_t num;
272ddb6f225SStephen Checkoway } SectorInfo;
273ddb6f225SStephen Checkoway 
27464659053SStephen Checkoway /*
27564659053SStephen Checkoway  * offset should be a byte offset of the QEMU device and _not_ a device
27664659053SStephen Checkoway  * offset.
27764659053SStephen Checkoway  */
pflash_sector_info(PFlashCFI02 * pfl,hwaddr offset)278ddb6f225SStephen Checkoway static SectorInfo pflash_sector_info(PFlashCFI02 *pfl, hwaddr offset)
27964659053SStephen Checkoway {
28064659053SStephen Checkoway     assert(offset < pfl->chip_len);
28164659053SStephen Checkoway     hwaddr addr = 0;
282ddb6f225SStephen Checkoway     uint32_t sector_num = 0;
283102f0f79SPhilippe Mathieu-Daudé     for (int i = 0; i < pflash_regions_count(pfl); ++i) {
28464659053SStephen Checkoway         uint64_t region_size = (uint64_t)pfl->nb_blocs[i] * pfl->sector_len[i];
28564659053SStephen Checkoway         if (addr <= offset && offset < addr + region_size) {
286ddb6f225SStephen Checkoway             return (SectorInfo) {
287ddb6f225SStephen Checkoway                 .len = pfl->sector_len[i],
288ddb6f225SStephen Checkoway                 .num = sector_num + (offset - addr) / pfl->sector_len[i],
289ddb6f225SStephen Checkoway             };
29064659053SStephen Checkoway         }
291ddb6f225SStephen Checkoway         sector_num += pfl->nb_blocs[i];
29264659053SStephen Checkoway         addr += region_size;
29364659053SStephen Checkoway     }
29464659053SStephen Checkoway     abort();
29564659053SStephen Checkoway }
29664659053SStephen Checkoway 
297ddb6f225SStephen Checkoway /*
298ddb6f225SStephen Checkoway  * Returns true if the offset refers to a flash sector that is currently being
299ddb6f225SStephen Checkoway  * erased.
300ddb6f225SStephen Checkoway  */
pflash_sector_is_erasing(PFlashCFI02 * pfl,hwaddr offset)301ddb6f225SStephen Checkoway static bool pflash_sector_is_erasing(PFlashCFI02 *pfl, hwaddr offset)
302ddb6f225SStephen Checkoway {
303ddb6f225SStephen Checkoway     long sector_num = pflash_sector_info(pfl, offset).num;
304ddb6f225SStephen Checkoway     return test_bit(sector_num, pfl->sector_erase_map);
305ddb6f225SStephen Checkoway }
306ddb6f225SStephen Checkoway 
pflash_read(void * opaque,hwaddr offset,unsigned int width)307aff498cfSPhilippe Mathieu-Daudé static uint64_t pflash_read(void *opaque, hwaddr offset, unsigned int width)
30849ab747fSPaolo Bonzini {
309aff498cfSPhilippe Mathieu-Daudé     PFlashCFI02 *pfl = opaque;
31049ab747fSPaolo Bonzini     hwaddr boff;
311aff498cfSPhilippe Mathieu-Daudé     uint64_t ret;
31249ab747fSPaolo Bonzini 
31349ab747fSPaolo Bonzini     /* Lazy reset to ROMD mode after a certain amount of read accesses */
31449ab747fSPaolo Bonzini     if (!pfl->rom_mode && pfl->wcycle == 0 &&
31549ab747fSPaolo Bonzini         ++pfl->read_counter > PFLASH_LAZY_ROMD_THRESHOLD) {
3167cb10960SPhilippe Mathieu-Daudé         pflash_mode_read_array(pfl);
31749ab747fSPaolo Bonzini     }
31849ab747fSPaolo Bonzini     offset &= pfl->chip_len - 1;
31949ab747fSPaolo Bonzini     boff = offset & 0xFF;
32064659053SStephen Checkoway     if (pfl->width == 2) {
32149ab747fSPaolo Bonzini         boff = boff >> 1;
32251500d37SPhilippe Mathieu-Daudé     } else if (pfl->width == 4) {
32351500d37SPhilippe Mathieu-Daudé         boff = boff >> 2;
32464659053SStephen Checkoway     }
32549ab747fSPaolo Bonzini     switch (pfl->cmd) {
32649ab747fSPaolo Bonzini     default:
32749ab747fSPaolo Bonzini         /* This should never happen : reset state & treat it as a read*/
32891316cbbSDavid Edmondson         trace_pflash_read_unknown_state(pfl->name, pfl->cmd);
3297d1df53fSPhilippe Mathieu-Daudé         pflash_reset_state_machine(pfl);
33049ab747fSPaolo Bonzini         /* fall through to the read code */
331b0349937SPhilippe Mathieu-Daudé     case 0x80: /* Erase (unlock) */
33249ab747fSPaolo Bonzini         /* We accept reads during second unlock sequence... */
33349ab747fSPaolo Bonzini     case 0x00:
334ddb6f225SStephen Checkoway         if (pflash_erase_suspend_mode(pfl) &&
335ddb6f225SStephen Checkoway             pflash_sector_is_erasing(pfl, offset)) {
336ddb6f225SStephen Checkoway             /* Toggle bit 2, but not 6. */
337ddb6f225SStephen Checkoway             toggle_dq2(pfl);
338ddb6f225SStephen Checkoway             /* Status register read */
339ddb6f225SStephen Checkoway             ret = pfl->status;
34091316cbbSDavid Edmondson             trace_pflash_read_status(pfl->name, ret);
341ddb6f225SStephen Checkoway             break;
342ddb6f225SStephen Checkoway         }
34349ab747fSPaolo Bonzini         /* Flash area read */
34406e8b8e3SPhilippe Mathieu-Daudé         ret = pflash_data_read(pfl, offset, width);
34549ab747fSPaolo Bonzini         break;
346b0349937SPhilippe Mathieu-Daudé     case 0x90: /* flash ID read */
34749ab747fSPaolo Bonzini         switch (boff) {
34849ab747fSPaolo Bonzini         case 0x00:
34949ab747fSPaolo Bonzini         case 0x01:
35049ab747fSPaolo Bonzini             ret = boff & 0x01 ? pfl->ident1 : pfl->ident0;
35149ab747fSPaolo Bonzini             break;
35249ab747fSPaolo Bonzini         case 0x02:
35349ab747fSPaolo Bonzini             ret = 0x00; /* Pretend all sectors are unprotected */
35449ab747fSPaolo Bonzini             break;
35549ab747fSPaolo Bonzini         case 0x0E:
35649ab747fSPaolo Bonzini         case 0x0F:
35749ab747fSPaolo Bonzini             ret = boff & 0x01 ? pfl->ident3 : pfl->ident2;
3587f7bdcafSPhilippe Mathieu-Daudé             if (ret != (uint8_t)-1) {
35949ab747fSPaolo Bonzini                 break;
3607f7bdcafSPhilippe Mathieu-Daudé             }
3617f7bdcafSPhilippe Mathieu-Daudé             /* Fall through to data read. */
36249ab747fSPaolo Bonzini         default:
36306e8b8e3SPhilippe Mathieu-Daudé             ret = pflash_data_read(pfl, offset, width);
36449ab747fSPaolo Bonzini         }
36591316cbbSDavid Edmondson         trace_pflash_read_done(pfl->name, boff, ret);
36649ab747fSPaolo Bonzini         break;
367b0349937SPhilippe Mathieu-Daudé     case 0x10: /* Chip Erase */
368b0349937SPhilippe Mathieu-Daudé     case 0x30: /* Sector Erase */
369ddb6f225SStephen Checkoway         /* Toggle bit 2 during erase, but not program. */
370ddb6f225SStephen Checkoway         toggle_dq2(pfl);
3712658594fSPhilippe Mathieu-Daudé         /* fall through */
372b0349937SPhilippe Mathieu-Daudé     case 0xA0: /* Program */
373ddb6f225SStephen Checkoway         /* Toggle bit 6 */
374ddb6f225SStephen Checkoway         toggle_dq6(pfl);
37549ab747fSPaolo Bonzini         /* Status register read */
37649ab747fSPaolo Bonzini         ret = pfl->status;
37791316cbbSDavid Edmondson         trace_pflash_read_status(pfl->name, ret);
37849ab747fSPaolo Bonzini         break;
37949ab747fSPaolo Bonzini     case 0x98:
38049ab747fSPaolo Bonzini         /* CFI query mode */
38107c13a71SPhilippe Mathieu-Daudé         if (boff < sizeof(pfl->cfi_table)) {
38249ab747fSPaolo Bonzini             ret = pfl->cfi_table[boff];
38307c13a71SPhilippe Mathieu-Daudé         } else {
38407c13a71SPhilippe Mathieu-Daudé             ret = 0;
38507c13a71SPhilippe Mathieu-Daudé         }
38649ab747fSPaolo Bonzini         break;
38749ab747fSPaolo Bonzini     }
38891316cbbSDavid Edmondson     trace_pflash_io_read(pfl->name, offset, width, ret, pfl->cmd, pfl->wcycle);
38949ab747fSPaolo Bonzini 
39049ab747fSPaolo Bonzini     return ret;
39149ab747fSPaolo Bonzini }
39249ab747fSPaolo Bonzini 
39349ab747fSPaolo Bonzini /* update flash content on disk */
pflash_update(PFlashCFI02 * pfl,int offset,int size)394aff498cfSPhilippe Mathieu-Daudé static void pflash_update(PFlashCFI02 *pfl, int offset, int size)
39549ab747fSPaolo Bonzini {
39649ab747fSPaolo Bonzini     int offset_end;
3971857b9dbSMansour Ahmadi     int ret;
3984be74634SMarkus Armbruster     if (pfl->blk) {
39949ab747fSPaolo Bonzini         offset_end = offset + size;
400098e732dSEric Blake         /* widen to sector boundaries */
401098e732dSEric Blake         offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE);
402098e732dSEric Blake         offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE);
403a9262f55SAlberto Faria         ret = blk_pwrite(pfl->blk, offset, offset_end - offset,
404a9262f55SAlberto Faria                          pfl->storage + offset, 0);
4051857b9dbSMansour Ahmadi         if (ret < 0) {
4061857b9dbSMansour Ahmadi             /* TODO set error bit in status */
4071857b9dbSMansour Ahmadi             error_report("Could not update PFLASH: %s", strerror(-ret));
4081857b9dbSMansour Ahmadi         }
40949ab747fSPaolo Bonzini     }
41049ab747fSPaolo Bonzini }
41149ab747fSPaolo Bonzini 
pflash_sector_erase(PFlashCFI02 * pfl,hwaddr offset)412a50547acSStephen Checkoway static void pflash_sector_erase(PFlashCFI02 *pfl, hwaddr offset)
413a50547acSStephen Checkoway {
414ddb6f225SStephen Checkoway     SectorInfo sector_info = pflash_sector_info(pfl, offset);
415ddb6f225SStephen Checkoway     uint64_t sector_len = sector_info.len;
416a50547acSStephen Checkoway     offset &= ~(sector_len - 1);
41791316cbbSDavid Edmondson     trace_pflash_sector_erase_start(pfl->name, pfl->width * 2, offset,
418a50547acSStephen Checkoway                                     pfl->width * 2, offset + sector_len - 1);
419a50547acSStephen Checkoway     if (!pfl->ro) {
420a50547acSStephen Checkoway         uint8_t *p = pfl->storage;
421a50547acSStephen Checkoway         memset(p + offset, 0xff, sector_len);
422a50547acSStephen Checkoway         pflash_update(pfl, offset, sector_len);
423a50547acSStephen Checkoway     }
424a50547acSStephen Checkoway     set_dq7(pfl, 0x00);
425a50547acSStephen Checkoway     ++pfl->sectors_to_erase;
426ddb6f225SStephen Checkoway     set_bit(sector_info.num, pfl->sector_erase_map);
427a50547acSStephen Checkoway     /* Set (or reset) the 50 us timer for additional erase commands.  */
428a50547acSStephen Checkoway     timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 50000);
429a50547acSStephen Checkoway }
430a50547acSStephen Checkoway 
pflash_write(void * opaque,hwaddr offset,uint64_t value,unsigned int width)431aff498cfSPhilippe Mathieu-Daudé static void pflash_write(void *opaque, hwaddr offset, uint64_t value,
432aff498cfSPhilippe Mathieu-Daudé                          unsigned int width)
43349ab747fSPaolo Bonzini {
434aff498cfSPhilippe Mathieu-Daudé     PFlashCFI02 *pfl = opaque;
43549ab747fSPaolo Bonzini     hwaddr boff;
43649ab747fSPaolo Bonzini     uint8_t *p;
43749ab747fSPaolo Bonzini     uint8_t cmd;
43849ab747fSPaolo Bonzini 
43991316cbbSDavid Edmondson     trace_pflash_io_write(pfl->name, offset, width, value, pfl->wcycle);
44049ab747fSPaolo Bonzini     cmd = value;
4418a508e70SPhilippe Mathieu-Daudé     if (pfl->cmd != 0xA0) {
442a9791042SStephen Checkoway         /* Reset does nothing during chip erase and sector erase. */
443a9791042SStephen Checkoway         if (cmd == 0xF0 && pfl->cmd != 0x10 && pfl->cmd != 0x30) {
44446fb7809SStephen Checkoway             if (pfl->wcycle == WCYCLE_AUTOSELECT_CFI) {
44546fb7809SStephen Checkoway                 /* Return to autoselect mode. */
44646fb7809SStephen Checkoway                 pfl->wcycle = 3;
44746fb7809SStephen Checkoway                 pfl->cmd = 0x90;
44846fb7809SStephen Checkoway                 return;
44946fb7809SStephen Checkoway             }
45049ab747fSPaolo Bonzini             goto reset_flash;
45149ab747fSPaolo Bonzini         }
4528a508e70SPhilippe Mathieu-Daudé     }
45349ab747fSPaolo Bonzini     offset &= pfl->chip_len - 1;
45449ab747fSPaolo Bonzini 
4556682bc1eSStephen Checkoway     boff = offset;
45664659053SStephen Checkoway     if (pfl->width == 2) {
45749ab747fSPaolo Bonzini         boff = boff >> 1;
45851500d37SPhilippe Mathieu-Daudé     } else if (pfl->width == 4) {
45951500d37SPhilippe Mathieu-Daudé         boff = boff >> 2;
46064659053SStephen Checkoway     }
4616682bc1eSStephen Checkoway     /* Only the least-significant 11 bits are used in most cases. */
4626682bc1eSStephen Checkoway     boff &= 0x7FF;
46349ab747fSPaolo Bonzini     switch (pfl->wcycle) {
46449ab747fSPaolo Bonzini     case 0:
46549ab747fSPaolo Bonzini         /* Set the device in I/O access mode if required */
466cadf25cfSPhilippe Mathieu-Daudé         if (pfl->rom_mode) {
467cadf25cfSPhilippe Mathieu-Daudé             pfl->rom_mode = false;
468cadf25cfSPhilippe Mathieu-Daudé             memory_region_rom_device_set_romd(&pfl->orig_mem, false);
469cadf25cfSPhilippe Mathieu-Daudé         }
47049ab747fSPaolo Bonzini         pfl->read_counter = 0;
47149ab747fSPaolo Bonzini         /* We're in read mode */
47249ab747fSPaolo Bonzini     check_unlock0:
47349ab747fSPaolo Bonzini         if (boff == 0x55 && cmd == 0x98) {
47449ab747fSPaolo Bonzini             /* Enter CFI query mode */
475aeaf6c20SPhilippe Mathieu-Daudé             pfl->wcycle = WCYCLE_CFI;
47649ab747fSPaolo Bonzini             pfl->cmd = 0x98;
47749ab747fSPaolo Bonzini             return;
47849ab747fSPaolo Bonzini         }
479ddb6f225SStephen Checkoway         /* Handle erase resume in erase suspend mode, otherwise reset. */
480b0349937SPhilippe Mathieu-Daudé         if (cmd == 0x30) { /* Erase Resume */
481ddb6f225SStephen Checkoway             if (pflash_erase_suspend_mode(pfl)) {
482ddb6f225SStephen Checkoway                 /* Resume the erase. */
483ddb6f225SStephen Checkoway                 timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
484ddb6f225SStephen Checkoway                           pfl->erase_time_remaining);
485ddb6f225SStephen Checkoway                 pfl->erase_time_remaining = 0;
486ddb6f225SStephen Checkoway                 pfl->wcycle = 6;
487ddb6f225SStephen Checkoway                 pfl->cmd = 0x30;
488ddb6f225SStephen Checkoway                 set_dq7(pfl, 0x00);
489ddb6f225SStephen Checkoway                 assert_dq3(pfl);
490ddb6f225SStephen Checkoway                 return;
491ddb6f225SStephen Checkoway             }
492ddb6f225SStephen Checkoway             goto reset_flash;
493ddb6f225SStephen Checkoway         }
494ddb6f225SStephen Checkoway         /* Ignore erase suspend. */
495b0349937SPhilippe Mathieu-Daudé         if (cmd == 0xB0) { /* Erase Suspend */
496ddb6f225SStephen Checkoway             return;
497ddb6f225SStephen Checkoway         }
49849ab747fSPaolo Bonzini         if (boff != pfl->unlock_addr0 || cmd != 0xAA) {
49991316cbbSDavid Edmondson             trace_pflash_unlock0_failed(pfl->name, boff,
50091316cbbSDavid Edmondson                                         cmd, pfl->unlock_addr0);
50149ab747fSPaolo Bonzini             goto reset_flash;
50249ab747fSPaolo Bonzini         }
50391316cbbSDavid Edmondson         trace_pflash_write(pfl->name, "unlock sequence started");
50449ab747fSPaolo Bonzini         break;
50549ab747fSPaolo Bonzini     case 1:
50649ab747fSPaolo Bonzini         /* We started an unlock sequence */
50749ab747fSPaolo Bonzini     check_unlock1:
50849ab747fSPaolo Bonzini         if (boff != pfl->unlock_addr1 || cmd != 0x55) {
50991316cbbSDavid Edmondson             trace_pflash_unlock1_failed(pfl->name, boff, cmd);
51049ab747fSPaolo Bonzini             goto reset_flash;
51149ab747fSPaolo Bonzini         }
51291316cbbSDavid Edmondson         trace_pflash_write(pfl->name, "unlock sequence done");
51349ab747fSPaolo Bonzini         break;
51449ab747fSPaolo Bonzini     case 2:
51549ab747fSPaolo Bonzini         /* We finished an unlock sequence */
51649ab747fSPaolo Bonzini         if (!pfl->bypass && boff != pfl->unlock_addr0) {
51791316cbbSDavid Edmondson             trace_pflash_write_failed(pfl->name, boff, cmd);
51849ab747fSPaolo Bonzini             goto reset_flash;
51949ab747fSPaolo Bonzini         }
52049ab747fSPaolo Bonzini         switch (cmd) {
52149ab747fSPaolo Bonzini         case 0x20:
52249ab747fSPaolo Bonzini             pfl->bypass = 1;
52349ab747fSPaolo Bonzini             goto do_bypass;
524b0349937SPhilippe Mathieu-Daudé         case 0x80: /* Erase */
525b0349937SPhilippe Mathieu-Daudé         case 0x90: /* Autoselect */
526b0349937SPhilippe Mathieu-Daudé         case 0xA0: /* Program */
52749ab747fSPaolo Bonzini             pfl->cmd = cmd;
52891316cbbSDavid Edmondson             trace_pflash_write_start(pfl->name, cmd);
52949ab747fSPaolo Bonzini             break;
53049ab747fSPaolo Bonzini         default:
53191316cbbSDavid Edmondson             trace_pflash_write_unknown(pfl->name, cmd);
53249ab747fSPaolo Bonzini             goto reset_flash;
53349ab747fSPaolo Bonzini         }
53449ab747fSPaolo Bonzini         break;
53549ab747fSPaolo Bonzini     case 3:
53649ab747fSPaolo Bonzini         switch (pfl->cmd) {
537b0349937SPhilippe Mathieu-Daudé         case 0x80: /* Erase */
53849ab747fSPaolo Bonzini             /* We need another unlock sequence */
53949ab747fSPaolo Bonzini             goto check_unlock0;
540b0349937SPhilippe Mathieu-Daudé         case 0xA0: /* Program */
541ddb6f225SStephen Checkoway             if (pflash_erase_suspend_mode(pfl) &&
542ddb6f225SStephen Checkoway                 pflash_sector_is_erasing(pfl, offset)) {
543ddb6f225SStephen Checkoway                 /* Ignore writes to erasing sectors. */
544ddb6f225SStephen Checkoway                 if (pfl->bypass) {
545ddb6f225SStephen Checkoway                     goto do_bypass;
546ddb6f225SStephen Checkoway                 }
547ddb6f225SStephen Checkoway                 goto reset_flash;
548ddb6f225SStephen Checkoway             }
549284a7ee2SGerd Hoffmann             trace_pflash_data_write(pfl->name, offset, width, value);
55049ab747fSPaolo Bonzini             if (!pfl->ro) {
551c3d25271SPhilippe Mathieu-Daudé                 p = (uint8_t *)pfl->storage + offset;
552c3d25271SPhilippe Mathieu-Daudé                 if (pfl->be) {
553c3d25271SPhilippe Mathieu-Daudé                     uint64_t current = ldn_be_p(p, width);
554c3d25271SPhilippe Mathieu-Daudé                     stn_be_p(p, width, current & value);
55549ab747fSPaolo Bonzini                 } else {
556c3d25271SPhilippe Mathieu-Daudé                     uint64_t current = ldn_le_p(p, width);
557c3d25271SPhilippe Mathieu-Daudé                     stn_le_p(p, width, current & value);
55849ab747fSPaolo Bonzini                 }
559c3d25271SPhilippe Mathieu-Daudé                 pflash_update(pfl, offset, width);
56049ab747fSPaolo Bonzini             }
5611d311e73SPhilippe Mathieu-Daudé             /*
5621d311e73SPhilippe Mathieu-Daudé              * While programming, status bit DQ7 should hold the opposite
5631d311e73SPhilippe Mathieu-Daudé              * value from how it was programmed.
5641d311e73SPhilippe Mathieu-Daudé              */
5651d311e73SPhilippe Mathieu-Daudé             set_dq7(pfl, ~value);
56649ab747fSPaolo Bonzini             /* Let's pretend write is immediate */
56749ab747fSPaolo Bonzini             if (pfl->bypass)
56849ab747fSPaolo Bonzini                 goto do_bypass;
56949ab747fSPaolo Bonzini             goto reset_flash;
570b0349937SPhilippe Mathieu-Daudé         case 0x90: /* Autoselect */
57149ab747fSPaolo Bonzini             if (pfl->bypass && cmd == 0x00) {
57249ab747fSPaolo Bonzini                 /* Unlock bypass reset */
57349ab747fSPaolo Bonzini                 goto reset_flash;
57449ab747fSPaolo Bonzini             }
57546fb7809SStephen Checkoway             /*
57646fb7809SStephen Checkoway              * We can enter CFI query mode from autoselect mode, but we must
57746fb7809SStephen Checkoway              * return to autoselect mode after a reset.
57846fb7809SStephen Checkoway              */
57946fb7809SStephen Checkoway             if (boff == 0x55 && cmd == 0x98) {
58046fb7809SStephen Checkoway                 /* Enter autoselect CFI query mode */
58146fb7809SStephen Checkoway                 pfl->wcycle = WCYCLE_AUTOSELECT_CFI;
58246fb7809SStephen Checkoway                 pfl->cmd = 0x98;
58346fb7809SStephen Checkoway                 return;
58446fb7809SStephen Checkoway             }
585124e4cfaSPhilippe Mathieu-Daudé             /* fall through */
58649ab747fSPaolo Bonzini         default:
58791316cbbSDavid Edmondson             trace_pflash_write_invalid(pfl->name, pfl->cmd);
58849ab747fSPaolo Bonzini             goto reset_flash;
58949ab747fSPaolo Bonzini         }
59049ab747fSPaolo Bonzini     case 4:
59149ab747fSPaolo Bonzini         switch (pfl->cmd) {
592b0349937SPhilippe Mathieu-Daudé         case 0xA0: /* Program */
59349ab747fSPaolo Bonzini             /* Ignore writes while flash data write is occurring */
59449ab747fSPaolo Bonzini             /* As we suppose write is immediate, this should never happen */
59549ab747fSPaolo Bonzini             return;
596b0349937SPhilippe Mathieu-Daudé         case 0x80: /* Erase */
59749ab747fSPaolo Bonzini             goto check_unlock1;
59849ab747fSPaolo Bonzini         default:
59949ab747fSPaolo Bonzini             /* Should never happen */
60091316cbbSDavid Edmondson             trace_pflash_write_invalid_state(pfl->name, pfl->cmd, 5);
60149ab747fSPaolo Bonzini             goto reset_flash;
60249ab747fSPaolo Bonzini         }
60349ab747fSPaolo Bonzini         break;
60449ab747fSPaolo Bonzini     case 5:
605ddb6f225SStephen Checkoway         if (pflash_erase_suspend_mode(pfl)) {
606ddb6f225SStephen Checkoway             /* Erasing is not supported in erase suspend mode. */
607ddb6f225SStephen Checkoway             goto reset_flash;
608ddb6f225SStephen Checkoway         }
60949ab747fSPaolo Bonzini         switch (cmd) {
610b0349937SPhilippe Mathieu-Daudé         case 0x10: /* Chip Erase */
61149ab747fSPaolo Bonzini             if (boff != pfl->unlock_addr0) {
61291316cbbSDavid Edmondson                 trace_pflash_chip_erase_invalid(pfl->name, offset);
61349ab747fSPaolo Bonzini                 goto reset_flash;
61449ab747fSPaolo Bonzini             }
61549ab747fSPaolo Bonzini             /* Chip erase */
61691316cbbSDavid Edmondson             trace_pflash_chip_erase_start(pfl->name);
61749ab747fSPaolo Bonzini             if (!pfl->ro) {
6181eb27d69SPhilippe Mathieu-Daudé                 memset(pfl->storage, 0xff, pfl->chip_len);
61949ab747fSPaolo Bonzini                 pflash_update(pfl, 0, pfl->chip_len);
62049ab747fSPaolo Bonzini             }
6211d311e73SPhilippe Mathieu-Daudé             set_dq7(pfl, 0x00);
62280f2c625SStephen Checkoway             /* Wait the time specified at CFI address 0x22. */
623d80cf1ebSStephen Checkoway             timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
62480f2c625SStephen Checkoway                       (1ULL << pfl->cfi_table[0x22]) * SCALE_MS);
62549ab747fSPaolo Bonzini             break;
626b0349937SPhilippe Mathieu-Daudé         case 0x30: /* Sector erase */
627a50547acSStephen Checkoway             pflash_sector_erase(pfl, offset);
62849ab747fSPaolo Bonzini             break;
62949ab747fSPaolo Bonzini         default:
63091316cbbSDavid Edmondson             trace_pflash_write_invalid_command(pfl->name, cmd);
63149ab747fSPaolo Bonzini             goto reset_flash;
63249ab747fSPaolo Bonzini         }
63349ab747fSPaolo Bonzini         pfl->cmd = cmd;
63449ab747fSPaolo Bonzini         break;
63549ab747fSPaolo Bonzini     case 6:
63649ab747fSPaolo Bonzini         switch (pfl->cmd) {
637b0349937SPhilippe Mathieu-Daudé         case 0x10: /* Chip Erase */
63849ab747fSPaolo Bonzini             /* Ignore writes during chip erase */
63949ab747fSPaolo Bonzini             return;
640b0349937SPhilippe Mathieu-Daudé         case 0x30: /* Sector erase */
641ddb6f225SStephen Checkoway             if (cmd == 0xB0) {
642ddb6f225SStephen Checkoway                 /*
643ddb6f225SStephen Checkoway                  * If erase suspend happens during the erase timeout (so DQ3 is
644ddb6f225SStephen Checkoway                  * 0), then the device suspends erasing immediately. Set the
645ddb6f225SStephen Checkoway                  * remaining time to be the total time to erase. Otherwise,
646ddb6f225SStephen Checkoway                  * there is a maximum amount of time it can take to enter
647ddb6f225SStephen Checkoway                  * suspend mode. Let's ignore that and suspend immediately and
648ddb6f225SStephen Checkoway                  * set the remaining time to the actual time remaining on the
649ddb6f225SStephen Checkoway                  * timer.
650ddb6f225SStephen Checkoway                  */
651ddb6f225SStephen Checkoway                 if ((pfl->status & 0x08) == 0) {
652ddb6f225SStephen Checkoway                     pfl->erase_time_remaining = pflash_erase_time(pfl);
653ddb6f225SStephen Checkoway                 } else {
654ddb6f225SStephen Checkoway                     int64_t delta = timer_expire_time_ns(&pfl->timer) -
655ddb6f225SStephen Checkoway                         qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
656ddb6f225SStephen Checkoway                     /* Make sure we have a positive time remaining. */
657ddb6f225SStephen Checkoway                     pfl->erase_time_remaining = delta <= 0 ? 1 : delta;
658ddb6f225SStephen Checkoway                 }
659ddb6f225SStephen Checkoway                 reset_dq3(pfl);
660ddb6f225SStephen Checkoway                 timer_del(&pfl->timer);
6617d1df53fSPhilippe Mathieu-Daudé                 pflash_reset_state_machine(pfl);
662ddb6f225SStephen Checkoway                 return;
663ddb6f225SStephen Checkoway             }
664a50547acSStephen Checkoway             /*
665a50547acSStephen Checkoway              * If DQ3 is 0, additional sector erase commands can be
666a50547acSStephen Checkoway              * written and anything else (other than an erase suspend) resets
667a50547acSStephen Checkoway              * the device.
668a50547acSStephen Checkoway              */
669a50547acSStephen Checkoway             if ((pfl->status & 0x08) == 0) {
670a50547acSStephen Checkoway                 if (cmd == 0x30) {
671a50547acSStephen Checkoway                     pflash_sector_erase(pfl, offset);
672a50547acSStephen Checkoway                 } else {
673a50547acSStephen Checkoway                     goto reset_flash;
674a50547acSStephen Checkoway                 }
675a50547acSStephen Checkoway             }
676a50547acSStephen Checkoway             /* Ignore writes during the actual erase. */
67749ab747fSPaolo Bonzini             return;
67849ab747fSPaolo Bonzini         default:
67949ab747fSPaolo Bonzini             /* Should never happen */
68091316cbbSDavid Edmondson             trace_pflash_write_invalid_state(pfl->name, pfl->cmd, 6);
68149ab747fSPaolo Bonzini             goto reset_flash;
68249ab747fSPaolo Bonzini         }
68349ab747fSPaolo Bonzini         break;
684aeaf6c20SPhilippe Mathieu-Daudé     /* Special values for CFI queries */
685aeaf6c20SPhilippe Mathieu-Daudé     case WCYCLE_CFI:
68646fb7809SStephen Checkoway     case WCYCLE_AUTOSELECT_CFI:
68791316cbbSDavid Edmondson         trace_pflash_write(pfl->name, "invalid write in CFI query mode");
68849ab747fSPaolo Bonzini         goto reset_flash;
68949ab747fSPaolo Bonzini     default:
69049ab747fSPaolo Bonzini         /* Should never happen */
69191316cbbSDavid Edmondson         trace_pflash_write(pfl->name, "invalid write state (wc 7)");
69249ab747fSPaolo Bonzini         goto reset_flash;
69349ab747fSPaolo Bonzini     }
69449ab747fSPaolo Bonzini     pfl->wcycle++;
69549ab747fSPaolo Bonzini 
69649ab747fSPaolo Bonzini     return;
69749ab747fSPaolo Bonzini 
69849ab747fSPaolo Bonzini     /* Reset flash */
69949ab747fSPaolo Bonzini  reset_flash:
70049ab747fSPaolo Bonzini     pfl->bypass = 0;
7017d1df53fSPhilippe Mathieu-Daudé     pflash_reset_state_machine(pfl);
70249ab747fSPaolo Bonzini     return;
70349ab747fSPaolo Bonzini 
70449ab747fSPaolo Bonzini  do_bypass:
70549ab747fSPaolo Bonzini     pfl->wcycle = 2;
70649ab747fSPaolo Bonzini     pfl->cmd = 0;
70749ab747fSPaolo Bonzini }
70849ab747fSPaolo Bonzini 
709aff498cfSPhilippe Mathieu-Daudé static const MemoryRegionOps pflash_cfi02_ops = {
710aff498cfSPhilippe Mathieu-Daudé     .read = pflash_read,
711aff498cfSPhilippe Mathieu-Daudé     .write = pflash_write,
712a4afb28dSPeter Maydell     .valid.min_access_size = 1,
713a4afb28dSPeter Maydell     .valid.max_access_size = 4,
71449ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
71549ab747fSPaolo Bonzini };
71649ab747fSPaolo Bonzini 
pflash_cfi02_fill_cfi_table(PFlashCFI02 * pfl,int nb_regions)7174586c2e5SPhilippe Mathieu-Daudé static void pflash_cfi02_fill_cfi_table(PFlashCFI02 *pfl, int nb_regions)
7184586c2e5SPhilippe Mathieu-Daudé {
7194586c2e5SPhilippe Mathieu-Daudé     /* Hardcoded CFI table (mostly from SG29 Spansion flash) */
7204586c2e5SPhilippe Mathieu-Daudé     const uint16_t pri_ofs = 0x40;
7214586c2e5SPhilippe Mathieu-Daudé     /* Standard "QRY" string */
7224586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x10] = 'Q';
7234586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x11] = 'R';
7244586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x12] = 'Y';
7254586c2e5SPhilippe Mathieu-Daudé     /* Command set (AMD/Fujitsu) */
7264586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x13] = 0x02;
7274586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x14] = 0x00;
7284586c2e5SPhilippe Mathieu-Daudé     /* Primary extended table address */
7294586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x15] = pri_ofs;
7304586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x16] = pri_ofs >> 8;
7314586c2e5SPhilippe Mathieu-Daudé     /* Alternate command set (none) */
7324586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x17] = 0x00;
7334586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x18] = 0x00;
7344586c2e5SPhilippe Mathieu-Daudé     /* Alternate extended table (none) */
7354586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x19] = 0x00;
7364586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x1A] = 0x00;
7374586c2e5SPhilippe Mathieu-Daudé     /* Vcc min */
7384586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x1B] = 0x27;
7394586c2e5SPhilippe Mathieu-Daudé     /* Vcc max */
7404586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x1C] = 0x36;
7414586c2e5SPhilippe Mathieu-Daudé     /* Vpp min (no Vpp pin) */
7424586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x1D] = 0x00;
7434586c2e5SPhilippe Mathieu-Daudé     /* Vpp max (no Vpp pin) */
7444586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x1E] = 0x00;
7454586c2e5SPhilippe Mathieu-Daudé     /* Timeout per single byte/word write (128 ms) */
7464586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x1F] = 0x07;
7474586c2e5SPhilippe Mathieu-Daudé     /* Timeout for min size buffer write (NA) */
7484586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x20] = 0x00;
7494586c2e5SPhilippe Mathieu-Daudé     /* Typical timeout for block erase (512 ms) */
7504586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x21] = 0x09;
7514586c2e5SPhilippe Mathieu-Daudé     /* Typical timeout for full chip erase (4096 ms) */
7524586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x22] = 0x0C;
7534586c2e5SPhilippe Mathieu-Daudé     /* Reserved */
7544586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x23] = 0x01;
7554586c2e5SPhilippe Mathieu-Daudé     /* Max timeout for buffer write (NA) */
7564586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x24] = 0x00;
7574586c2e5SPhilippe Mathieu-Daudé     /* Max timeout for block erase */
7584586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x25] = 0x0A;
7594586c2e5SPhilippe Mathieu-Daudé     /* Max timeout for chip erase */
7604586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x26] = 0x0D;
7614586c2e5SPhilippe Mathieu-Daudé     /* Device size */
7624586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x27] = ctz32(pfl->chip_len);
7634586c2e5SPhilippe Mathieu-Daudé     /* Flash device interface (8 & 16 bits) */
7644586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x28] = 0x02;
7654586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x29] = 0x00;
7664586c2e5SPhilippe Mathieu-Daudé     /* Max number of bytes in multi-bytes write */
7674586c2e5SPhilippe Mathieu-Daudé     /*
7684586c2e5SPhilippe Mathieu-Daudé      * XXX: disable buffered write as it's not supported
7694586c2e5SPhilippe Mathieu-Daudé      * pfl->cfi_table[0x2A] = 0x05;
7704586c2e5SPhilippe Mathieu-Daudé      */
7714586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x2A] = 0x00;
7724586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x2B] = 0x00;
7734586c2e5SPhilippe Mathieu-Daudé     /* Number of erase block regions */
7744586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x2c] = nb_regions;
7754586c2e5SPhilippe Mathieu-Daudé     /* Erase block regions */
7764586c2e5SPhilippe Mathieu-Daudé     for (int i = 0; i < nb_regions; ++i) {
7774586c2e5SPhilippe Mathieu-Daudé         uint32_t sector_len_per_device = pfl->sector_len[i];
7784586c2e5SPhilippe Mathieu-Daudé         pfl->cfi_table[0x2d + 4 * i] = pfl->nb_blocs[i] - 1;
7794586c2e5SPhilippe Mathieu-Daudé         pfl->cfi_table[0x2e + 4 * i] = (pfl->nb_blocs[i] - 1) >> 8;
7804586c2e5SPhilippe Mathieu-Daudé         pfl->cfi_table[0x2f + 4 * i] = sector_len_per_device >> 8;
7814586c2e5SPhilippe Mathieu-Daudé         pfl->cfi_table[0x30 + 4 * i] = sector_len_per_device >> 16;
7824586c2e5SPhilippe Mathieu-Daudé     }
7834586c2e5SPhilippe Mathieu-Daudé     assert(0x2c + 4 * nb_regions < pri_ofs);
7844586c2e5SPhilippe Mathieu-Daudé 
7854586c2e5SPhilippe Mathieu-Daudé     /* Extended */
7864586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x00 + pri_ofs] = 'P';
7874586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x01 + pri_ofs] = 'R';
7884586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x02 + pri_ofs] = 'I';
7894586c2e5SPhilippe Mathieu-Daudé 
7904586c2e5SPhilippe Mathieu-Daudé     /* Extended version 1.0 */
7914586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x03 + pri_ofs] = '1';
7924586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x04 + pri_ofs] = '0';
7934586c2e5SPhilippe Mathieu-Daudé 
7944586c2e5SPhilippe Mathieu-Daudé     /* Address sensitive unlock required. */
7954586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x05 + pri_ofs] = 0x00;
7964586c2e5SPhilippe Mathieu-Daudé     /* Erase suspend to read/write. */
7974586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x06 + pri_ofs] = 0x02;
7984586c2e5SPhilippe Mathieu-Daudé     /* Sector protect not supported. */
7994586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x07 + pri_ofs] = 0x00;
8004586c2e5SPhilippe Mathieu-Daudé     /* Temporary sector unprotect not supported. */
8014586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x08 + pri_ofs] = 0x00;
8024586c2e5SPhilippe Mathieu-Daudé 
8034586c2e5SPhilippe Mathieu-Daudé     /* Sector protect/unprotect scheme. */
8044586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x09 + pri_ofs] = 0x00;
8054586c2e5SPhilippe Mathieu-Daudé 
8064586c2e5SPhilippe Mathieu-Daudé     /* Simultaneous operation not supported. */
8074586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x0a + pri_ofs] = 0x00;
8084586c2e5SPhilippe Mathieu-Daudé     /* Burst mode not supported. */
8094586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x0b + pri_ofs] = 0x00;
8104586c2e5SPhilippe Mathieu-Daudé     /* Page mode not supported. */
8114586c2e5SPhilippe Mathieu-Daudé     pfl->cfi_table[0x0c + pri_ofs] = 0x00;
8124586c2e5SPhilippe Mathieu-Daudé     assert(0x0c + pri_ofs < ARRAY_SIZE(pfl->cfi_table));
8134586c2e5SPhilippe Mathieu-Daudé }
8144586c2e5SPhilippe Mathieu-Daudé 
pflash_cfi02_realize(DeviceState * dev,Error ** errp)815da3bd642SHu Tao static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
81649ab747fSPaolo Bonzini {
81776612456SVladimir Sementsov-Ogievskiy     ERRP_GUARD();
818e7b62741SMarkus Armbruster     PFlashCFI02 *pfl = PFLASH_CFI02(dev);
81949ab747fSPaolo Bonzini     int ret;
82049ab747fSPaolo Bonzini 
82164659053SStephen Checkoway     if (pfl->uniform_sector_len == 0 && pfl->sector_len[0] == 0) {
8228929fc3aSZiyue Yang         error_setg(errp, "attribute \"sector-length\" not specified or zero.");
8238929fc3aSZiyue Yang         return;
8248929fc3aSZiyue Yang     }
82564659053SStephen Checkoway     if (pfl->uniform_nb_blocs == 0 && pfl->nb_blocs[0] == 0) {
8268929fc3aSZiyue Yang         error_setg(errp, "attribute \"num-blocks\" not specified or zero.");
8278929fc3aSZiyue Yang         return;
8288929fc3aSZiyue Yang     }
8298929fc3aSZiyue Yang     if (pfl->name == NULL) {
8308929fc3aSZiyue Yang         error_setg(errp, "attribute \"name\" not specified.");
8318929fc3aSZiyue Yang         return;
8328929fc3aSZiyue Yang     }
8338929fc3aSZiyue Yang 
83464659053SStephen Checkoway     int nb_regions;
83564659053SStephen Checkoway     pfl->chip_len = 0;
836ddb6f225SStephen Checkoway     pfl->total_sectors = 0;
83764659053SStephen Checkoway     for (nb_regions = 0; nb_regions < PFLASH_MAX_ERASE_REGIONS; ++nb_regions) {
83864659053SStephen Checkoway         if (pfl->nb_blocs[nb_regions] == 0) {
83964659053SStephen Checkoway             break;
84064659053SStephen Checkoway         }
841ddb6f225SStephen Checkoway         pfl->total_sectors += pfl->nb_blocs[nb_regions];
84264659053SStephen Checkoway         uint64_t sector_len_per_device = pfl->sector_len[nb_regions];
84364659053SStephen Checkoway 
84464659053SStephen Checkoway         /*
84564659053SStephen Checkoway          * The size of each flash sector must be a power of 2 and it must be
84664659053SStephen Checkoway          * aligned at the same power of 2.
84764659053SStephen Checkoway          */
84864659053SStephen Checkoway         if (sector_len_per_device & 0xff ||
84964659053SStephen Checkoway             sector_len_per_device >= (1 << 24) ||
85064659053SStephen Checkoway             !is_power_of_2(sector_len_per_device))
85164659053SStephen Checkoway         {
85264659053SStephen Checkoway             error_setg(errp, "unsupported configuration: "
85364659053SStephen Checkoway                        "sector length[%d] per device = %" PRIx64 ".",
85464659053SStephen Checkoway                        nb_regions, sector_len_per_device);
85564659053SStephen Checkoway             return;
85664659053SStephen Checkoway         }
85764659053SStephen Checkoway         if (pfl->chip_len & (sector_len_per_device - 1)) {
85864659053SStephen Checkoway             error_setg(errp, "unsupported configuration: "
85964659053SStephen Checkoway                        "flash region %d not correctly aligned.",
86064659053SStephen Checkoway                        nb_regions);
86164659053SStephen Checkoway             return;
86264659053SStephen Checkoway         }
86364659053SStephen Checkoway 
86464659053SStephen Checkoway         pfl->chip_len += (uint64_t)pfl->sector_len[nb_regions] *
86564659053SStephen Checkoway                           pfl->nb_blocs[nb_regions];
86664659053SStephen Checkoway     }
86764659053SStephen Checkoway 
86864659053SStephen Checkoway     uint64_t uniform_len = (uint64_t)pfl->uniform_nb_blocs *
86964659053SStephen Checkoway                            pfl->uniform_sector_len;
87064659053SStephen Checkoway     if (nb_regions == 0) {
87164659053SStephen Checkoway         nb_regions = 1;
87264659053SStephen Checkoway         pfl->nb_blocs[0] = pfl->uniform_nb_blocs;
87364659053SStephen Checkoway         pfl->sector_len[0] = pfl->uniform_sector_len;
87464659053SStephen Checkoway         pfl->chip_len = uniform_len;
875ddb6f225SStephen Checkoway         pfl->total_sectors = pfl->uniform_nb_blocs;
87664659053SStephen Checkoway     } else if (uniform_len != 0 && uniform_len != pfl->chip_len) {
87764659053SStephen Checkoway         error_setg(errp, "\"num-blocks\"*\"sector-length\" "
87864659053SStephen Checkoway                    "different from \"num-blocks0\"*\'sector-length0\" + ... + "
87964659053SStephen Checkoway                    "\"num-blocks3\"*\"sector-length3\"");
88064659053SStephen Checkoway         return;
88164659053SStephen Checkoway     }
88249ab747fSPaolo Bonzini 
883aff498cfSPhilippe Mathieu-Daudé     memory_region_init_rom_device(&pfl->orig_mem, OBJECT(pfl),
884aff498cfSPhilippe Mathieu-Daudé                                   &pflash_cfi02_ops, pfl, pfl->name,
88576612456SVladimir Sementsov-Ogievskiy                                   pfl->chip_len, errp);
88676612456SVladimir Sementsov-Ogievskiy     if (*errp) {
88733e0eb52SHu Tao         return;
88833e0eb52SHu Tao     }
88933e0eb52SHu Tao 
89049ab747fSPaolo Bonzini     pfl->storage = memory_region_get_ram_ptr(&pfl->orig_mem);
891a17c17a2SKevin Wolf 
892a17c17a2SKevin Wolf     if (pfl->blk) {
893a17c17a2SKevin Wolf         uint64_t perm;
89486b1cf32SKevin Wolf         pfl->ro = !blk_supports_write_perm(pfl->blk);
895a17c17a2SKevin Wolf         perm = BLK_PERM_CONSISTENT_READ | (pfl->ro ? 0 : BLK_PERM_WRITE);
896a17c17a2SKevin Wolf         ret = blk_set_perm(pfl->blk, perm, BLK_PERM_ALL, errp);
897a17c17a2SKevin Wolf         if (ret < 0) {
898a17c17a2SKevin Wolf             return;
899a17c17a2SKevin Wolf         }
900a17c17a2SKevin Wolf     } else {
901a17c17a2SKevin Wolf         pfl->ro = 0;
902a17c17a2SKevin Wolf     }
903a17c17a2SKevin Wolf 
9044be74634SMarkus Armbruster     if (pfl->blk) {
905*954b33daSManos Pitsidianakis         if (!blk_check_size_and_read_all(pfl->blk, dev, pfl->storage,
9061eb27d69SPhilippe Mathieu-Daudé                                          pfl->chip_len, errp)) {
907da3bd642SHu Tao             vmstate_unregister_ram(&pfl->orig_mem, DEVICE(pfl));
908da3bd642SHu Tao             return;
90949ab747fSPaolo Bonzini         }
91049ab747fSPaolo Bonzini     }
91149ab747fSPaolo Bonzini 
9126682bc1eSStephen Checkoway     /* Only 11 bits are used in the comparison. */
9136682bc1eSStephen Checkoway     pfl->unlock_addr0 &= 0x7FF;
9146682bc1eSStephen Checkoway     pfl->unlock_addr1 &= 0x7FF;
9156682bc1eSStephen Checkoway 
916ddb6f225SStephen Checkoway     /* Allocate memory for a bitmap for sectors being erased. */
917ddb6f225SStephen Checkoway     pfl->sector_erase_map = bitmap_new(pfl->total_sectors);
918ddb6f225SStephen Checkoway 
9191d4ae5a3SPhilippe Mathieu-Daudé     pfl->rom_mode = true;
92027545c9dSPhilippe Mathieu-Daudé     if (pfl->mappings > 1) {
92149ab747fSPaolo Bonzini         pflash_setup_mappings(pfl);
922da3bd642SHu Tao         sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem);
92327545c9dSPhilippe Mathieu-Daudé     } else {
92427545c9dSPhilippe Mathieu-Daudé         sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->orig_mem);
92527545c9dSPhilippe Mathieu-Daudé     }
92649ab747fSPaolo Bonzini 
927d80cf1ebSStephen Checkoway     timer_init_ns(&pfl->timer, QEMU_CLOCK_VIRTUAL, pflash_timer, pfl);
92849ab747fSPaolo Bonzini     pfl->status = 0;
9299ac45b88SPhilippe Mathieu-Daudé 
9304586c2e5SPhilippe Mathieu-Daudé     pflash_cfi02_fill_cfi_table(pfl, nb_regions);
93149ab747fSPaolo Bonzini }
93249ab747fSPaolo Bonzini 
pflash_cfi02_reset(DeviceState * dev)933d9106341SPhilippe Mathieu-Daudé static void pflash_cfi02_reset(DeviceState *dev)
934d9106341SPhilippe Mathieu-Daudé {
935d9106341SPhilippe Mathieu-Daudé     PFlashCFI02 *pfl = PFLASH_CFI02(dev);
936d9106341SPhilippe Mathieu-Daudé 
937d9106341SPhilippe Mathieu-Daudé     pflash_reset_state_machine(pfl);
938d9106341SPhilippe Mathieu-Daudé }
939d9106341SPhilippe Mathieu-Daudé 
94049ab747fSPaolo Bonzini static Property pflash_cfi02_properties[] = {
94116434065SMarkus Armbruster     DEFINE_PROP_DRIVE("drive", PFlashCFI02, blk),
94264659053SStephen Checkoway     DEFINE_PROP_UINT32("num-blocks", PFlashCFI02, uniform_nb_blocs, 0),
94364659053SStephen Checkoway     DEFINE_PROP_UINT32("sector-length", PFlashCFI02, uniform_sector_len, 0),
94464659053SStephen Checkoway     DEFINE_PROP_UINT32("num-blocks0", PFlashCFI02, nb_blocs[0], 0),
94564659053SStephen Checkoway     DEFINE_PROP_UINT32("sector-length0", PFlashCFI02, sector_len[0], 0),
94664659053SStephen Checkoway     DEFINE_PROP_UINT32("num-blocks1", PFlashCFI02, nb_blocs[1], 0),
94764659053SStephen Checkoway     DEFINE_PROP_UINT32("sector-length1", PFlashCFI02, sector_len[1], 0),
94864659053SStephen Checkoway     DEFINE_PROP_UINT32("num-blocks2", PFlashCFI02, nb_blocs[2], 0),
94964659053SStephen Checkoway     DEFINE_PROP_UINT32("sector-length2", PFlashCFI02, sector_len[2], 0),
95064659053SStephen Checkoway     DEFINE_PROP_UINT32("num-blocks3", PFlashCFI02, nb_blocs[3], 0),
95164659053SStephen Checkoway     DEFINE_PROP_UINT32("sector-length3", PFlashCFI02, sector_len[3], 0),
95216434065SMarkus Armbruster     DEFINE_PROP_UINT8("width", PFlashCFI02, width, 0),
95316434065SMarkus Armbruster     DEFINE_PROP_UINT8("mappings", PFlashCFI02, mappings, 0),
95416434065SMarkus Armbruster     DEFINE_PROP_UINT8("big-endian", PFlashCFI02, be, 0),
95516434065SMarkus Armbruster     DEFINE_PROP_UINT16("id0", PFlashCFI02, ident0, 0),
95616434065SMarkus Armbruster     DEFINE_PROP_UINT16("id1", PFlashCFI02, ident1, 0),
95716434065SMarkus Armbruster     DEFINE_PROP_UINT16("id2", PFlashCFI02, ident2, 0),
95816434065SMarkus Armbruster     DEFINE_PROP_UINT16("id3", PFlashCFI02, ident3, 0),
95916434065SMarkus Armbruster     DEFINE_PROP_UINT16("unlock-addr0", PFlashCFI02, unlock_addr0, 0),
96016434065SMarkus Armbruster     DEFINE_PROP_UINT16("unlock-addr1", PFlashCFI02, unlock_addr1, 0),
96116434065SMarkus Armbruster     DEFINE_PROP_STRING("name", PFlashCFI02, name),
96249ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
96349ab747fSPaolo Bonzini };
96449ab747fSPaolo Bonzini 
pflash_cfi02_unrealize(DeviceState * dev)965b69c3c21SMarkus Armbruster static void pflash_cfi02_unrealize(DeviceState *dev)
966d80cf1ebSStephen Checkoway {
967e7b62741SMarkus Armbruster     PFlashCFI02 *pfl = PFLASH_CFI02(dev);
968d80cf1ebSStephen Checkoway     timer_del(&pfl->timer);
969ddb6f225SStephen Checkoway     g_free(pfl->sector_erase_map);
970d80cf1ebSStephen Checkoway }
971d80cf1ebSStephen Checkoway 
pflash_cfi02_class_init(ObjectClass * klass,void * data)97249ab747fSPaolo Bonzini static void pflash_cfi02_class_init(ObjectClass *klass, void *data)
97349ab747fSPaolo Bonzini {
97449ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
97549ab747fSPaolo Bonzini 
976da3bd642SHu Tao     dc->realize = pflash_cfi02_realize;
977d9106341SPhilippe Mathieu-Daudé     dc->reset = pflash_cfi02_reset;
978d80cf1ebSStephen Checkoway     dc->unrealize = pflash_cfi02_unrealize;
9794f67d30bSMarc-André Lureau     device_class_set_props(dc, pflash_cfi02_properties);
980df6f9318SAntony Pavlov     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
98149ab747fSPaolo Bonzini }
98249ab747fSPaolo Bonzini 
98349ab747fSPaolo Bonzini static const TypeInfo pflash_cfi02_info = {
984e7b62741SMarkus Armbruster     .name           = TYPE_PFLASH_CFI02,
98549ab747fSPaolo Bonzini     .parent         = TYPE_SYS_BUS_DEVICE,
98616434065SMarkus Armbruster     .instance_size  = sizeof(PFlashCFI02),
98749ab747fSPaolo Bonzini     .class_init     = pflash_cfi02_class_init,
98849ab747fSPaolo Bonzini };
98949ab747fSPaolo Bonzini 
pflash_cfi02_register_types(void)99049ab747fSPaolo Bonzini static void pflash_cfi02_register_types(void)
99149ab747fSPaolo Bonzini {
99249ab747fSPaolo Bonzini     type_register_static(&pflash_cfi02_info);
99349ab747fSPaolo Bonzini }
99449ab747fSPaolo Bonzini 
type_init(pflash_cfi02_register_types)99549ab747fSPaolo Bonzini type_init(pflash_cfi02_register_types)
99649ab747fSPaolo Bonzini 
99716434065SMarkus Armbruster PFlashCFI02 *pflash_cfi02_register(hwaddr base,
998940d5b13SMarkus Armbruster                                    const char *name,
99949ab747fSPaolo Bonzini                                    hwaddr size,
100016434065SMarkus Armbruster                                    BlockBackend *blk,
1001ce14710fSMarkus Armbruster                                    uint32_t sector_len,
100216434065SMarkus Armbruster                                    int nb_mappings, int width,
100349ab747fSPaolo Bonzini                                    uint16_t id0, uint16_t id1,
100449ab747fSPaolo Bonzini                                    uint16_t id2, uint16_t id3,
100516434065SMarkus Armbruster                                    uint16_t unlock_addr0,
100616434065SMarkus Armbruster                                    uint16_t unlock_addr1,
100749ab747fSPaolo Bonzini                                    int be)
100849ab747fSPaolo Bonzini {
10093e80f690SMarkus Armbruster     DeviceState *dev = qdev_new(TYPE_PFLASH_CFI02);
101049ab747fSPaolo Bonzini 
10119b3d111aSMarkus Armbruster     if (blk) {
1012934df912SMarkus Armbruster         qdev_prop_set_drive(dev, "drive", blk);
101349ab747fSPaolo Bonzini     }
10144cdd0a77SPhilippe Mathieu-Daudé     assert(QEMU_IS_ALIGNED(size, sector_len));
1015ce14710fSMarkus Armbruster     qdev_prop_set_uint32(dev, "num-blocks", size / sector_len);
101649ab747fSPaolo Bonzini     qdev_prop_set_uint32(dev, "sector-length", sector_len);
101749ab747fSPaolo Bonzini     qdev_prop_set_uint8(dev, "width", width);
101849ab747fSPaolo Bonzini     qdev_prop_set_uint8(dev, "mappings", nb_mappings);
101949ab747fSPaolo Bonzini     qdev_prop_set_uint8(dev, "big-endian", !!be);
102049ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id0", id0);
102149ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id1", id1);
102249ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id2", id2);
102349ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id3", id3);
102449ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "unlock-addr0", unlock_addr0);
102549ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "unlock-addr1", unlock_addr1);
102649ab747fSPaolo Bonzini     qdev_prop_set_string(dev, "name", name);
10273c6ef471SMarkus Armbruster     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
102849ab747fSPaolo Bonzini 
10293509c396SHu Tao     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
1030e7b62741SMarkus Armbruster     return PFLASH_CFI02(dev);
103149ab747fSPaolo Bonzini }
1032