xref: /qemu/tests/qtest/pflash-cfi02-test.c (revision 64659053557a6a19bb00bb226e9b4d8f78cead7b)
160b725b6SStephen Checkoway /*
260b725b6SStephen Checkoway  * QTest testcase for parallel flash with AMD command set
360b725b6SStephen Checkoway  *
460b725b6SStephen Checkoway  * Copyright (c) 2019 Stephen Checkoway
560b725b6SStephen Checkoway  *
660b725b6SStephen Checkoway  * This work is licensed under the terms of the GNU GPL, version 2 or later.
760b725b6SStephen Checkoway  * See the COPYING file in the top-level directory.
860b725b6SStephen Checkoway  */
960b725b6SStephen Checkoway 
1060b725b6SStephen Checkoway #include "qemu/osdep.h"
1160b725b6SStephen Checkoway #include "libqtest.h"
1260b725b6SStephen Checkoway 
1360b725b6SStephen Checkoway /*
1460b725b6SStephen Checkoway  * To test the pflash_cfi02 device, we run QEMU with the musicpal machine with
1560b725b6SStephen Checkoway  * a pflash drive. This enables us to test some flash configurations, but not
1660b725b6SStephen Checkoway  * all. In particular, we're limited to a 16-bit wide flash device.
1760b725b6SStephen Checkoway  */
1860b725b6SStephen Checkoway 
1960b725b6SStephen Checkoway #define MP_FLASH_SIZE_MAX (32 * 1024 * 1024)
2060b725b6SStephen Checkoway #define BASE_ADDR (0x100000000ULL - MP_FLASH_SIZE_MAX)
2160b725b6SStephen Checkoway 
22*64659053SStephen Checkoway #define UNIFORM_FLASH_SIZE (8 * 1024 * 1024)
23*64659053SStephen Checkoway #define UNIFORM_FLASH_SECTOR_SIZE (64 * 1024)
24*64659053SStephen Checkoway 
2591d02312SPhilippe Mathieu-Daudé /* Use a newtype to keep flash addresses separate from byte addresses. */
2691d02312SPhilippe Mathieu-Daudé typedef struct {
2791d02312SPhilippe Mathieu-Daudé     uint64_t addr;
2891d02312SPhilippe Mathieu-Daudé } faddr;
2991d02312SPhilippe Mathieu-Daudé #define FLASH_ADDR(x) ((faddr) { .addr = (x) })
3091d02312SPhilippe Mathieu-Daudé 
3191d02312SPhilippe Mathieu-Daudé #define CFI_ADDR FLASH_ADDR(0x55)
3291d02312SPhilippe Mathieu-Daudé #define UNLOCK0_ADDR FLASH_ADDR(0x555)
3391d02312SPhilippe Mathieu-Daudé #define UNLOCK1_ADDR FLASH_ADDR(0x2AA)
3460b725b6SStephen Checkoway 
3560b725b6SStephen Checkoway #define CFI_CMD 0x98
3660b725b6SStephen Checkoway #define UNLOCK0_CMD 0xAA
3760b725b6SStephen Checkoway #define UNLOCK1_CMD 0x55
3860b725b6SStephen Checkoway #define AUTOSELECT_CMD 0x90
3960b725b6SStephen Checkoway #define RESET_CMD 0xF0
4060b725b6SStephen Checkoway #define PROGRAM_CMD 0xA0
4160b725b6SStephen Checkoway #define SECTOR_ERASE_CMD 0x30
4260b725b6SStephen Checkoway #define CHIP_ERASE_CMD 0x10
4360b725b6SStephen Checkoway #define UNLOCK_BYPASS_CMD 0x20
4460b725b6SStephen Checkoway #define UNLOCK_BYPASS_RESET_CMD 0x00
4560b725b6SStephen Checkoway 
4691d02312SPhilippe Mathieu-Daudé typedef struct {
4791d02312SPhilippe Mathieu-Daudé     int bank_width;
4891d02312SPhilippe Mathieu-Daudé 
49*64659053SStephen Checkoway     /* Nonuniform block size. */
50*64659053SStephen Checkoway     int nb_blocs[4];
51*64659053SStephen Checkoway     int sector_len[4];
52*64659053SStephen Checkoway 
5391d02312SPhilippe Mathieu-Daudé     QTestState *qtest;
5491d02312SPhilippe Mathieu-Daudé } FlashConfig;
5591d02312SPhilippe Mathieu-Daudé 
5660b725b6SStephen Checkoway static char image_path[] = "/tmp/qtest.XXXXXX";
5760b725b6SStephen Checkoway 
5891d02312SPhilippe Mathieu-Daudé /*
5991d02312SPhilippe Mathieu-Daudé  * The pflash implementation allows some parameters to be unspecified. We want
6091d02312SPhilippe Mathieu-Daudé  * to test those configurations but we also need to know the real values in
6191d02312SPhilippe Mathieu-Daudé  * our testing code. So after we launch qemu, we'll need a new FlashConfig
6291d02312SPhilippe Mathieu-Daudé  * with the correct values filled in.
6391d02312SPhilippe Mathieu-Daudé  */
6491d02312SPhilippe Mathieu-Daudé static FlashConfig expand_config_defaults(const FlashConfig *c)
6560b725b6SStephen Checkoway {
6691d02312SPhilippe Mathieu-Daudé     FlashConfig ret = *c;
6791d02312SPhilippe Mathieu-Daudé 
6891d02312SPhilippe Mathieu-Daudé     if (ret.bank_width == 0) {
6991d02312SPhilippe Mathieu-Daudé         ret.bank_width = 2;
7060b725b6SStephen Checkoway     }
71*64659053SStephen Checkoway     if (ret.nb_blocs[0] == 0 && ret.sector_len[0] == 0) {
72*64659053SStephen Checkoway         ret.sector_len[0] = UNIFORM_FLASH_SECTOR_SIZE;
73*64659053SStephen Checkoway         ret.nb_blocs[0] = UNIFORM_FLASH_SIZE / UNIFORM_FLASH_SECTOR_SIZE;
74*64659053SStephen Checkoway     }
7560b725b6SStephen Checkoway 
7691d02312SPhilippe Mathieu-Daudé     /* XXX: Limitations of test harness. */
7791d02312SPhilippe Mathieu-Daudé     assert(ret.bank_width == 2);
7891d02312SPhilippe Mathieu-Daudé     return ret;
7960b725b6SStephen Checkoway }
8060b725b6SStephen Checkoway 
8191d02312SPhilippe Mathieu-Daudé /*
8291d02312SPhilippe Mathieu-Daudé  * Return a bit mask suitable for extracting the least significant
8391d02312SPhilippe Mathieu-Daudé  * status/query response from an interleaved response.
8491d02312SPhilippe Mathieu-Daudé  */
8591d02312SPhilippe Mathieu-Daudé static inline uint64_t device_mask(const FlashConfig *c)
8660b725b6SStephen Checkoway {
8791d02312SPhilippe Mathieu-Daudé     return (uint64_t)-1;
8860b725b6SStephen Checkoway }
8960b725b6SStephen Checkoway 
9091d02312SPhilippe Mathieu-Daudé /*
9191d02312SPhilippe Mathieu-Daudé  * Return a bit mask exactly as long as the bank_width.
9291d02312SPhilippe Mathieu-Daudé  */
9391d02312SPhilippe Mathieu-Daudé static inline uint64_t bank_mask(const FlashConfig *c)
9460b725b6SStephen Checkoway {
9591d02312SPhilippe Mathieu-Daudé     if (c->bank_width == 8) {
9691d02312SPhilippe Mathieu-Daudé         return (uint64_t)-1;
9791d02312SPhilippe Mathieu-Daudé     }
9891d02312SPhilippe Mathieu-Daudé     return (1ULL << (c->bank_width * 8)) - 1ULL;
9960b725b6SStephen Checkoway }
10060b725b6SStephen Checkoway 
10191d02312SPhilippe Mathieu-Daudé static inline void flash_write(const FlashConfig *c, uint64_t byte_addr,
10291d02312SPhilippe Mathieu-Daudé                                uint64_t data)
10360b725b6SStephen Checkoway {
10491d02312SPhilippe Mathieu-Daudé     /* Sanity check our tests. */
10591d02312SPhilippe Mathieu-Daudé     assert((data & ~bank_mask(c)) == 0);
10691d02312SPhilippe Mathieu-Daudé     uint64_t addr = BASE_ADDR + byte_addr;
10791d02312SPhilippe Mathieu-Daudé     switch (c->bank_width) {
10891d02312SPhilippe Mathieu-Daudé     case 1:
10991d02312SPhilippe Mathieu-Daudé         qtest_writeb(c->qtest, addr, data);
11091d02312SPhilippe Mathieu-Daudé         break;
11191d02312SPhilippe Mathieu-Daudé     case 2:
11291d02312SPhilippe Mathieu-Daudé         qtest_writew(c->qtest, addr, data);
11391d02312SPhilippe Mathieu-Daudé         break;
11491d02312SPhilippe Mathieu-Daudé     case 4:
11591d02312SPhilippe Mathieu-Daudé         qtest_writel(c->qtest, addr, data);
11691d02312SPhilippe Mathieu-Daudé         break;
11791d02312SPhilippe Mathieu-Daudé     case 8:
11891d02312SPhilippe Mathieu-Daudé         qtest_writeq(c->qtest, addr, data);
11991d02312SPhilippe Mathieu-Daudé         break;
12091d02312SPhilippe Mathieu-Daudé     default:
12191d02312SPhilippe Mathieu-Daudé         abort();
12291d02312SPhilippe Mathieu-Daudé     }
12360b725b6SStephen Checkoway }
12460b725b6SStephen Checkoway 
12591d02312SPhilippe Mathieu-Daudé static inline uint64_t flash_read(const FlashConfig *c, uint64_t byte_addr)
12691d02312SPhilippe Mathieu-Daudé {
12791d02312SPhilippe Mathieu-Daudé     uint64_t addr = BASE_ADDR + byte_addr;
12891d02312SPhilippe Mathieu-Daudé     switch (c->bank_width) {
12991d02312SPhilippe Mathieu-Daudé     case 1:
13091d02312SPhilippe Mathieu-Daudé         return qtest_readb(c->qtest, addr);
13191d02312SPhilippe Mathieu-Daudé     case 2:
13291d02312SPhilippe Mathieu-Daudé         return qtest_readw(c->qtest, addr);
13391d02312SPhilippe Mathieu-Daudé     case 4:
13491d02312SPhilippe Mathieu-Daudé         return qtest_readl(c->qtest, addr);
13591d02312SPhilippe Mathieu-Daudé     case 8:
13691d02312SPhilippe Mathieu-Daudé         return qtest_readq(c->qtest, addr);
13791d02312SPhilippe Mathieu-Daudé     default:
13891d02312SPhilippe Mathieu-Daudé         abort();
13991d02312SPhilippe Mathieu-Daudé     }
14091d02312SPhilippe Mathieu-Daudé }
14191d02312SPhilippe Mathieu-Daudé 
14291d02312SPhilippe Mathieu-Daudé /*
14391d02312SPhilippe Mathieu-Daudé  * Convert a flash address expressed in the maximum width of the device as a
14491d02312SPhilippe Mathieu-Daudé  * byte address.
14591d02312SPhilippe Mathieu-Daudé  */
14691d02312SPhilippe Mathieu-Daudé static inline uint64_t as_byte_addr(const FlashConfig *c, faddr flash_addr)
14791d02312SPhilippe Mathieu-Daudé {
14891d02312SPhilippe Mathieu-Daudé     /*
14991d02312SPhilippe Mathieu-Daudé      * Command addresses are always given as addresses in the maximum
15091d02312SPhilippe Mathieu-Daudé      * supported bus size for the flash chip. So an x8/x16 chip in x8 mode
15191d02312SPhilippe Mathieu-Daudé      * uses addresses 0xAAA and 0x555 to unlock because the least significant
15291d02312SPhilippe Mathieu-Daudé      * bit is ignored. (0x555 rather than 0x554 is traditional.)
15391d02312SPhilippe Mathieu-Daudé      *
15491d02312SPhilippe Mathieu-Daudé      * In general we need to multiply by the maximum device width.
15591d02312SPhilippe Mathieu-Daudé      */
15691d02312SPhilippe Mathieu-Daudé     return flash_addr.addr * c->bank_width;
15791d02312SPhilippe Mathieu-Daudé }
15891d02312SPhilippe Mathieu-Daudé 
15991d02312SPhilippe Mathieu-Daudé /*
16091d02312SPhilippe Mathieu-Daudé  * Return the command value or expected status replicated across all devices.
16191d02312SPhilippe Mathieu-Daudé  */
16291d02312SPhilippe Mathieu-Daudé static inline uint64_t replicate(const FlashConfig *c, uint64_t data)
16391d02312SPhilippe Mathieu-Daudé {
16491d02312SPhilippe Mathieu-Daudé     /* Sanity check our tests. */
16591d02312SPhilippe Mathieu-Daudé     assert((data & ~device_mask(c)) == 0);
16691d02312SPhilippe Mathieu-Daudé     return data;
16791d02312SPhilippe Mathieu-Daudé }
16891d02312SPhilippe Mathieu-Daudé 
16991d02312SPhilippe Mathieu-Daudé static inline void flash_cmd(const FlashConfig *c, faddr cmd_addr,
17091d02312SPhilippe Mathieu-Daudé                              uint8_t cmd)
17191d02312SPhilippe Mathieu-Daudé {
17291d02312SPhilippe Mathieu-Daudé     flash_write(c, as_byte_addr(c, cmd_addr), replicate(c, cmd));
17391d02312SPhilippe Mathieu-Daudé }
17491d02312SPhilippe Mathieu-Daudé 
17591d02312SPhilippe Mathieu-Daudé static inline uint64_t flash_query(const FlashConfig *c, faddr query_addr)
17691d02312SPhilippe Mathieu-Daudé {
17791d02312SPhilippe Mathieu-Daudé     return flash_read(c, as_byte_addr(c, query_addr));
17891d02312SPhilippe Mathieu-Daudé }
17991d02312SPhilippe Mathieu-Daudé 
18091d02312SPhilippe Mathieu-Daudé static inline uint64_t flash_query_1(const FlashConfig *c, faddr query_addr)
18191d02312SPhilippe Mathieu-Daudé {
18291d02312SPhilippe Mathieu-Daudé     return flash_query(c, query_addr) & device_mask(c);
18391d02312SPhilippe Mathieu-Daudé }
18491d02312SPhilippe Mathieu-Daudé 
18591d02312SPhilippe Mathieu-Daudé static void unlock(const FlashConfig *c)
18691d02312SPhilippe Mathieu-Daudé {
18791d02312SPhilippe Mathieu-Daudé     flash_cmd(c, UNLOCK0_ADDR, UNLOCK0_CMD);
18891d02312SPhilippe Mathieu-Daudé     flash_cmd(c, UNLOCK1_ADDR, UNLOCK1_CMD);
18991d02312SPhilippe Mathieu-Daudé }
19091d02312SPhilippe Mathieu-Daudé 
19191d02312SPhilippe Mathieu-Daudé static void reset(const FlashConfig *c)
19291d02312SPhilippe Mathieu-Daudé {
19391d02312SPhilippe Mathieu-Daudé     flash_cmd(c, FLASH_ADDR(0), RESET_CMD);
19491d02312SPhilippe Mathieu-Daudé }
19591d02312SPhilippe Mathieu-Daudé 
19691d02312SPhilippe Mathieu-Daudé static void sector_erase(const FlashConfig *c, uint64_t byte_addr)
19791d02312SPhilippe Mathieu-Daudé {
19891d02312SPhilippe Mathieu-Daudé     unlock(c);
19991d02312SPhilippe Mathieu-Daudé     flash_cmd(c, UNLOCK0_ADDR, 0x80);
20091d02312SPhilippe Mathieu-Daudé     unlock(c);
20191d02312SPhilippe Mathieu-Daudé     flash_write(c, byte_addr, replicate(c, SECTOR_ERASE_CMD));
20291d02312SPhilippe Mathieu-Daudé }
20391d02312SPhilippe Mathieu-Daudé 
20491d02312SPhilippe Mathieu-Daudé static void wait_for_completion(const FlashConfig *c, uint64_t byte_addr)
20560b725b6SStephen Checkoway {
20660b725b6SStephen Checkoway     /* If DQ6 is toggling, step the clock and ensure the toggle stops. */
20791d02312SPhilippe Mathieu-Daudé     const uint64_t dq6 = replicate(c, 0x40);
20891d02312SPhilippe Mathieu-Daudé     if ((flash_read(c, byte_addr) & dq6) ^ (flash_read(c, byte_addr) & dq6)) {
20960b725b6SStephen Checkoway         /* Wait for erase or program to finish. */
21091d02312SPhilippe Mathieu-Daudé         qtest_clock_step_next(c->qtest);
21160b725b6SStephen Checkoway         /* Ensure that DQ6 has stopped toggling. */
21291d02312SPhilippe Mathieu-Daudé         g_assert_cmphex(flash_read(c, byte_addr), ==, flash_read(c, byte_addr));
21360b725b6SStephen Checkoway     }
21460b725b6SStephen Checkoway }
21560b725b6SStephen Checkoway 
21691d02312SPhilippe Mathieu-Daudé static void bypass_program(const FlashConfig *c, uint64_t byte_addr,
21791d02312SPhilippe Mathieu-Daudé                            uint16_t data)
21860b725b6SStephen Checkoway {
21991d02312SPhilippe Mathieu-Daudé     flash_cmd(c, UNLOCK0_ADDR, PROGRAM_CMD);
22091d02312SPhilippe Mathieu-Daudé     flash_write(c, byte_addr, data);
22160b725b6SStephen Checkoway     /*
22260b725b6SStephen Checkoway      * Data isn't valid until DQ6 stops toggling. We don't model this as
22360b725b6SStephen Checkoway      * writes are immediate, but if this changes in the future, we can wait
22460b725b6SStephen Checkoway      * until the program is complete.
22560b725b6SStephen Checkoway      */
22691d02312SPhilippe Mathieu-Daudé     wait_for_completion(c, byte_addr);
22760b725b6SStephen Checkoway }
22860b725b6SStephen Checkoway 
22991d02312SPhilippe Mathieu-Daudé static void program(const FlashConfig *c, uint64_t byte_addr, uint16_t data)
23060b725b6SStephen Checkoway {
23191d02312SPhilippe Mathieu-Daudé     unlock(c);
23291d02312SPhilippe Mathieu-Daudé     bypass_program(c, byte_addr, data);
23360b725b6SStephen Checkoway }
23460b725b6SStephen Checkoway 
23591d02312SPhilippe Mathieu-Daudé static void chip_erase(const FlashConfig *c)
23660b725b6SStephen Checkoway {
23791d02312SPhilippe Mathieu-Daudé     unlock(c);
23891d02312SPhilippe Mathieu-Daudé     flash_cmd(c, UNLOCK0_ADDR, 0x80);
23991d02312SPhilippe Mathieu-Daudé     unlock(c);
24091d02312SPhilippe Mathieu-Daudé     flash_cmd(c, UNLOCK0_ADDR, CHIP_ERASE_CMD);
24160b725b6SStephen Checkoway }
24260b725b6SStephen Checkoway 
243*64659053SStephen Checkoway /*
244*64659053SStephen Checkoway  * Test flash commands with a variety of device geometry.
245*64659053SStephen Checkoway  */
246*64659053SStephen Checkoway static void test_geometry(const void *opaque)
24760b725b6SStephen Checkoway {
24891d02312SPhilippe Mathieu-Daudé     const FlashConfig *config = opaque;
24991d02312SPhilippe Mathieu-Daudé     QTestState *qtest;
25091d02312SPhilippe Mathieu-Daudé     qtest = qtest_initf("-M musicpal,accel=qtest"
251*64659053SStephen Checkoway                         " -drive if=pflash,file=%s,format=raw,copy-on-read"
252*64659053SStephen Checkoway                         /* Device geometry properties. */
253*64659053SStephen Checkoway                         " -global driver=cfi.pflash02,"
254*64659053SStephen Checkoway                         "property=num-blocks0,value=%d"
255*64659053SStephen Checkoway                         " -global driver=cfi.pflash02,"
256*64659053SStephen Checkoway                         "property=sector-length0,value=%d"
257*64659053SStephen Checkoway                         " -global driver=cfi.pflash02,"
258*64659053SStephen Checkoway                         "property=num-blocks1,value=%d"
259*64659053SStephen Checkoway                         " -global driver=cfi.pflash02,"
260*64659053SStephen Checkoway                         "property=sector-length1,value=%d"
261*64659053SStephen Checkoway                         " -global driver=cfi.pflash02,"
262*64659053SStephen Checkoway                         "property=num-blocks2,value=%d"
263*64659053SStephen Checkoway                         " -global driver=cfi.pflash02,"
264*64659053SStephen Checkoway                         "property=sector-length2,value=%d"
265*64659053SStephen Checkoway                         " -global driver=cfi.pflash02,"
266*64659053SStephen Checkoway                         "property=num-blocks3,value=%d"
267*64659053SStephen Checkoway                         " -global driver=cfi.pflash02,"
268*64659053SStephen Checkoway                         "property=sector-length3,value=%d",
269*64659053SStephen Checkoway                         image_path,
270*64659053SStephen Checkoway                         config->nb_blocs[0],
271*64659053SStephen Checkoway                         config->sector_len[0],
272*64659053SStephen Checkoway                         config->nb_blocs[1],
273*64659053SStephen Checkoway                         config->sector_len[1],
274*64659053SStephen Checkoway                         config->nb_blocs[2],
275*64659053SStephen Checkoway                         config->sector_len[2],
276*64659053SStephen Checkoway                         config->nb_blocs[3],
277*64659053SStephen Checkoway                         config->sector_len[3]);
27891d02312SPhilippe Mathieu-Daudé     FlashConfig explicit_config = expand_config_defaults(config);
27991d02312SPhilippe Mathieu-Daudé     explicit_config.qtest = qtest;
28091d02312SPhilippe Mathieu-Daudé     const FlashConfig *c = &explicit_config;
28191d02312SPhilippe Mathieu-Daudé 
28260b725b6SStephen Checkoway     /* Check the IDs. */
28391d02312SPhilippe Mathieu-Daudé     unlock(c);
28491d02312SPhilippe Mathieu-Daudé     flash_cmd(c, UNLOCK0_ADDR, AUTOSELECT_CMD);
28591d02312SPhilippe Mathieu-Daudé     g_assert_cmphex(flash_query(c, FLASH_ADDR(0)), ==, replicate(c, 0xBF));
28691d02312SPhilippe Mathieu-Daudé     if (c->bank_width >= 2) {
28791d02312SPhilippe Mathieu-Daudé         /*
28891d02312SPhilippe Mathieu-Daudé          * XXX: The ID returned by the musicpal flash chip is 16 bits which
28991d02312SPhilippe Mathieu-Daudé          * wouldn't happen with an 8-bit device. It would probably be best to
29091d02312SPhilippe Mathieu-Daudé          * prohibit addresses larger than the device width in pflash_cfi02.c,
29191d02312SPhilippe Mathieu-Daudé          * but then we couldn't test smaller device widths at all.
29291d02312SPhilippe Mathieu-Daudé          */
29391d02312SPhilippe Mathieu-Daudé         g_assert_cmphex(flash_query(c, FLASH_ADDR(1)), ==,
29491d02312SPhilippe Mathieu-Daudé                         replicate(c, 0x236D));
29591d02312SPhilippe Mathieu-Daudé     }
29691d02312SPhilippe Mathieu-Daudé     reset(c);
29760b725b6SStephen Checkoway 
29860b725b6SStephen Checkoway     /* Check the erase blocks. */
29991d02312SPhilippe Mathieu-Daudé     flash_cmd(c, CFI_ADDR, CFI_CMD);
30091d02312SPhilippe Mathieu-Daudé     g_assert_cmphex(flash_query(c, FLASH_ADDR(0x10)), ==, replicate(c, 'Q'));
30191d02312SPhilippe Mathieu-Daudé     g_assert_cmphex(flash_query(c, FLASH_ADDR(0x11)), ==, replicate(c, 'R'));
30291d02312SPhilippe Mathieu-Daudé     g_assert_cmphex(flash_query(c, FLASH_ADDR(0x12)), ==, replicate(c, 'Y'));
30360b725b6SStephen Checkoway 
30491d02312SPhilippe Mathieu-Daudé     /* Num erase regions. */
305*64659053SStephen Checkoway     int nb_erase_regions = flash_query_1(c, FLASH_ADDR(0x2C));
306*64659053SStephen Checkoway     g_assert_cmphex(nb_erase_regions, ==,
307*64659053SStephen Checkoway                     !!c->nb_blocs[0] + !!c->nb_blocs[1] + !!c->nb_blocs[2] +
308*64659053SStephen Checkoway                     !!c->nb_blocs[3]);
30991d02312SPhilippe Mathieu-Daudé 
310*64659053SStephen Checkoway     /* Check device length. */
311*64659053SStephen Checkoway     uint32_t device_len = 1 << flash_query_1(c, FLASH_ADDR(0x27));
312*64659053SStephen Checkoway     g_assert_cmphex(device_len, ==, UNIFORM_FLASH_SIZE);
313*64659053SStephen Checkoway 
31491d02312SPhilippe Mathieu-Daudé     reset(c);
31591d02312SPhilippe Mathieu-Daudé 
31691d02312SPhilippe Mathieu-Daudé     const uint64_t dq7 = replicate(c, 0x80);
31791d02312SPhilippe Mathieu-Daudé     const uint64_t dq6 = replicate(c, 0x40);
318*64659053SStephen Checkoway     uint64_t byte_addr = 0;
319*64659053SStephen Checkoway     for (int region = 0; region < nb_erase_regions; ++region) {
320*64659053SStephen Checkoway         uint64_t base = 0x2D + 4 * region;
321*64659053SStephen Checkoway         flash_cmd(c, CFI_ADDR, CFI_CMD);
322*64659053SStephen Checkoway         uint32_t nb_sectors = flash_query_1(c, FLASH_ADDR(base + 0)) +
323*64659053SStephen Checkoway                               (flash_query_1(c, FLASH_ADDR(base + 1)) << 8) + 1;
324*64659053SStephen Checkoway         uint32_t sector_len = (flash_query_1(c, FLASH_ADDR(base + 2)) << 8) +
325*64659053SStephen Checkoway                               (flash_query_1(c, FLASH_ADDR(base + 3)) << 16);
326*64659053SStephen Checkoway         g_assert_cmphex(nb_sectors, ==, c->nb_blocs[region]);
327*64659053SStephen Checkoway         g_assert_cmphex(sector_len, ==, c->sector_len[region]);
328*64659053SStephen Checkoway         reset(c);
329*64659053SStephen Checkoway 
33060b725b6SStephen Checkoway         /* Erase and program sector. */
33160b725b6SStephen Checkoway         for (uint32_t i = 0; i < nb_sectors; ++i) {
33291d02312SPhilippe Mathieu-Daudé             sector_erase(c, byte_addr);
33360b725b6SStephen Checkoway             /* Read toggle. */
33491d02312SPhilippe Mathieu-Daudé             uint64_t status0 = flash_read(c, byte_addr);
33560b725b6SStephen Checkoway             /* DQ7 is 0 during an erase. */
33691d02312SPhilippe Mathieu-Daudé             g_assert_cmphex(status0 & dq7, ==, 0);
33791d02312SPhilippe Mathieu-Daudé             uint64_t status1 = flash_read(c, byte_addr);
33860b725b6SStephen Checkoway             /* DQ6 toggles during an erase. */
33991d02312SPhilippe Mathieu-Daudé             g_assert_cmphex(status0 & dq6, ==, ~status1 & dq6);
34060b725b6SStephen Checkoway             /* Wait for erase to complete. */
34191d02312SPhilippe Mathieu-Daudé             qtest_clock_step_next(c->qtest);
34260b725b6SStephen Checkoway             /* Ensure DQ6 has stopped toggling. */
343*64659053SStephen Checkoway             g_assert_cmphex(flash_read(c, byte_addr), ==,
344*64659053SStephen Checkoway                             flash_read(c, byte_addr));
34560b725b6SStephen Checkoway             /* Now the data should be valid. */
34691d02312SPhilippe Mathieu-Daudé             g_assert_cmphex(flash_read(c, byte_addr), ==, bank_mask(c));
34760b725b6SStephen Checkoway 
34860b725b6SStephen Checkoway             /* Program a bit pattern. */
34991d02312SPhilippe Mathieu-Daudé             program(c, byte_addr, 0x55);
35091d02312SPhilippe Mathieu-Daudé             g_assert_cmphex(flash_read(c, byte_addr) & 0xFF, ==, 0x55);
35191d02312SPhilippe Mathieu-Daudé             program(c, byte_addr, 0xA5);
35291d02312SPhilippe Mathieu-Daudé             g_assert_cmphex(flash_read(c, byte_addr) & 0xFF, ==, 0x05);
353*64659053SStephen Checkoway             byte_addr += sector_len;
354*64659053SStephen Checkoway         }
35560b725b6SStephen Checkoway     }
35660b725b6SStephen Checkoway 
35760b725b6SStephen Checkoway     /* Erase the chip. */
35891d02312SPhilippe Mathieu-Daudé     chip_erase(c);
35960b725b6SStephen Checkoway     /* Read toggle. */
36091d02312SPhilippe Mathieu-Daudé     uint64_t status0 = flash_read(c, 0);
36160b725b6SStephen Checkoway     /* DQ7 is 0 during an erase. */
36291d02312SPhilippe Mathieu-Daudé     g_assert_cmphex(status0 & dq7, ==, 0);
36391d02312SPhilippe Mathieu-Daudé     uint64_t status1 = flash_read(c, 0);
36460b725b6SStephen Checkoway     /* DQ6 toggles during an erase. */
36591d02312SPhilippe Mathieu-Daudé     g_assert_cmphex(status0 & dq6, ==, ~status1 & dq6);
36660b725b6SStephen Checkoway     /* Wait for erase to complete. */
36791d02312SPhilippe Mathieu-Daudé     qtest_clock_step_next(c->qtest);
36860b725b6SStephen Checkoway     /* Ensure DQ6 has stopped toggling. */
36991d02312SPhilippe Mathieu-Daudé     g_assert_cmphex(flash_read(c, 0), ==, flash_read(c, 0));
37060b725b6SStephen Checkoway     /* Now the data should be valid. */
37191d02312SPhilippe Mathieu-Daudé 
372*64659053SStephen Checkoway     for (int region = 0; region < nb_erase_regions; ++region) {
373*64659053SStephen Checkoway         for (uint32_t i = 0; i < c->nb_blocs[region]; ++i) {
374*64659053SStephen Checkoway             uint64_t byte_addr = i * c->sector_len[region];
37591d02312SPhilippe Mathieu-Daudé             g_assert_cmphex(flash_read(c, byte_addr), ==, bank_mask(c));
37691d02312SPhilippe Mathieu-Daudé         }
377*64659053SStephen Checkoway     }
37860b725b6SStephen Checkoway 
37960b725b6SStephen Checkoway     /* Unlock bypass */
38091d02312SPhilippe Mathieu-Daudé     unlock(c);
38191d02312SPhilippe Mathieu-Daudé     flash_cmd(c, UNLOCK0_ADDR, UNLOCK_BYPASS_CMD);
38291d02312SPhilippe Mathieu-Daudé     bypass_program(c, 0 * c->bank_width, 0x01);
38391d02312SPhilippe Mathieu-Daudé     bypass_program(c, 1 * c->bank_width, 0x23);
38491d02312SPhilippe Mathieu-Daudé     bypass_program(c, 2 * c->bank_width, 0x45);
38560b725b6SStephen Checkoway     /*
38660b725b6SStephen Checkoway      * Test that bypass programming, unlike normal programming can use any
38760b725b6SStephen Checkoway      * address for the PROGRAM_CMD.
38860b725b6SStephen Checkoway      */
38991d02312SPhilippe Mathieu-Daudé     flash_cmd(c, FLASH_ADDR(3 * c->bank_width), PROGRAM_CMD);
39091d02312SPhilippe Mathieu-Daudé     flash_write(c, 3 * c->bank_width, 0x67);
39191d02312SPhilippe Mathieu-Daudé     wait_for_completion(c, 3 * c->bank_width);
39291d02312SPhilippe Mathieu-Daudé     flash_cmd(c, FLASH_ADDR(0), UNLOCK_BYPASS_RESET_CMD);
39391d02312SPhilippe Mathieu-Daudé     bypass_program(c, 4 * c->bank_width, 0x89); /* Should fail. */
39491d02312SPhilippe Mathieu-Daudé     g_assert_cmphex(flash_read(c, 0 * c->bank_width), ==, 0x01);
39591d02312SPhilippe Mathieu-Daudé     g_assert_cmphex(flash_read(c, 1 * c->bank_width), ==, 0x23);
39691d02312SPhilippe Mathieu-Daudé     g_assert_cmphex(flash_read(c, 2 * c->bank_width), ==, 0x45);
39791d02312SPhilippe Mathieu-Daudé     g_assert_cmphex(flash_read(c, 3 * c->bank_width), ==, 0x67);
39891d02312SPhilippe Mathieu-Daudé     g_assert_cmphex(flash_read(c, 4 * c->bank_width), ==, bank_mask(c));
39960b725b6SStephen Checkoway 
4006682bc1eSStephen Checkoway     /* Test ignored high order bits of address. */
40191d02312SPhilippe Mathieu-Daudé     flash_cmd(c, FLASH_ADDR(0x5555), UNLOCK0_CMD);
40291d02312SPhilippe Mathieu-Daudé     flash_cmd(c, FLASH_ADDR(0x2AAA), UNLOCK1_CMD);
40391d02312SPhilippe Mathieu-Daudé     flash_cmd(c, FLASH_ADDR(0x5555), AUTOSELECT_CMD);
40491d02312SPhilippe Mathieu-Daudé     g_assert_cmphex(flash_query(c, FLASH_ADDR(0)), ==, replicate(c, 0xBF));
40591d02312SPhilippe Mathieu-Daudé     reset(c);
4066682bc1eSStephen Checkoway 
40791d02312SPhilippe Mathieu-Daudé     qtest_quit(qtest);
40860b725b6SStephen Checkoway }
40960b725b6SStephen Checkoway 
41060b725b6SStephen Checkoway static void cleanup(void *opaque)
41160b725b6SStephen Checkoway {
41260b725b6SStephen Checkoway     unlink(image_path);
41360b725b6SStephen Checkoway }
41460b725b6SStephen Checkoway 
41591d02312SPhilippe Mathieu-Daudé /*
41691d02312SPhilippe Mathieu-Daudé  * XXX: Tests are limited to bank_width = 2 for now because that's what
41791d02312SPhilippe Mathieu-Daudé  * hw/arm/musicpal.c has.
41891d02312SPhilippe Mathieu-Daudé  */
41991d02312SPhilippe Mathieu-Daudé static const FlashConfig configuration[] = {
42091d02312SPhilippe Mathieu-Daudé     /* One x16 device. */
42191d02312SPhilippe Mathieu-Daudé     {
42291d02312SPhilippe Mathieu-Daudé         .bank_width = 2,
42391d02312SPhilippe Mathieu-Daudé     },
424*64659053SStephen Checkoway     /* Nonuniform sectors (top boot). */
425*64659053SStephen Checkoway     {
426*64659053SStephen Checkoway         .bank_width = 2,
427*64659053SStephen Checkoway         .nb_blocs = { 127, 1, 2, 1 },
428*64659053SStephen Checkoway         .sector_len = { 0x10000, 0x08000, 0x02000, 0x04000 },
429*64659053SStephen Checkoway     },
430*64659053SStephen Checkoway     /* Nonuniform sectors (bottom boot). */
431*64659053SStephen Checkoway     {
432*64659053SStephen Checkoway         .bank_width = 2,
433*64659053SStephen Checkoway         .nb_blocs = { 1, 2, 1, 127 },
434*64659053SStephen Checkoway         .sector_len = { 0x04000, 0x02000, 0x08000, 0x10000 },
435*64659053SStephen Checkoway     },
43691d02312SPhilippe Mathieu-Daudé };
43791d02312SPhilippe Mathieu-Daudé 
43860b725b6SStephen Checkoway int main(int argc, char **argv)
43960b725b6SStephen Checkoway {
44060b725b6SStephen Checkoway     int fd = mkstemp(image_path);
44160b725b6SStephen Checkoway     if (fd == -1) {
44260b725b6SStephen Checkoway         g_printerr("Failed to create temporary file %s: %s\n", image_path,
44360b725b6SStephen Checkoway                    strerror(errno));
44460b725b6SStephen Checkoway         exit(EXIT_FAILURE);
44560b725b6SStephen Checkoway     }
446*64659053SStephen Checkoway     if (ftruncate(fd, UNIFORM_FLASH_SIZE) < 0) {
44760b725b6SStephen Checkoway         int error_code = errno;
44860b725b6SStephen Checkoway         close(fd);
44960b725b6SStephen Checkoway         unlink(image_path);
45091d02312SPhilippe Mathieu-Daudé         g_printerr("Failed to truncate file %s to %u MB: %s\n", image_path,
451*64659053SStephen Checkoway                    UNIFORM_FLASH_SIZE, strerror(error_code));
45260b725b6SStephen Checkoway         exit(EXIT_FAILURE);
45360b725b6SStephen Checkoway     }
45460b725b6SStephen Checkoway     close(fd);
45560b725b6SStephen Checkoway 
45660b725b6SStephen Checkoway     qtest_add_abrt_handler(cleanup, NULL);
45760b725b6SStephen Checkoway     g_test_init(&argc, &argv, NULL);
45891d02312SPhilippe Mathieu-Daudé 
45991d02312SPhilippe Mathieu-Daudé     size_t nb_configurations = sizeof configuration / sizeof configuration[0];
46091d02312SPhilippe Mathieu-Daudé     for (size_t i = 0; i < nb_configurations; ++i) {
46191d02312SPhilippe Mathieu-Daudé         const FlashConfig *config = &configuration[i];
462*64659053SStephen Checkoway         char *path = g_strdup_printf("pflash-cfi02"
463*64659053SStephen Checkoway                                      "/geometry/%dx%x-%dx%x-%dx%x-%dx%x"
464*64659053SStephen Checkoway                                      "/%d",
465*64659053SStephen Checkoway                                      config->nb_blocs[0],
466*64659053SStephen Checkoway                                      config->sector_len[0],
467*64659053SStephen Checkoway                                      config->nb_blocs[1],
468*64659053SStephen Checkoway                                      config->sector_len[1],
469*64659053SStephen Checkoway                                      config->nb_blocs[2],
470*64659053SStephen Checkoway                                      config->sector_len[2],
471*64659053SStephen Checkoway                                      config->nb_blocs[3],
472*64659053SStephen Checkoway                                      config->sector_len[3],
47391d02312SPhilippe Mathieu-Daudé                                      config->bank_width);
474*64659053SStephen Checkoway         qtest_add_data_func(path, config, test_geometry);
47591d02312SPhilippe Mathieu-Daudé         g_free(path);
47691d02312SPhilippe Mathieu-Daudé     }
47760b725b6SStephen Checkoway     int result = g_test_run();
47860b725b6SStephen Checkoway     cleanup(NULL);
47960b725b6SStephen Checkoway     return result;
48060b725b6SStephen Checkoway }
481