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