17a2334f7SCédric Le Goater /* 27a2334f7SCédric Le Goater * QTest testcase for the M25P80 Flash (Using the Aspeed SPI 37a2334f7SCédric Le Goater * Controller) 47a2334f7SCédric Le Goater * 57a2334f7SCédric Le Goater * Copyright (C) 2016 IBM Corp. 67a2334f7SCédric Le Goater * 77a2334f7SCédric Le Goater * Permission is hereby granted, free of charge, to any person obtaining a copy 87a2334f7SCédric Le Goater * of this software and associated documentation files (the "Software"), to deal 97a2334f7SCédric Le Goater * in the Software without restriction, including without limitation the rights 107a2334f7SCédric Le Goater * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 117a2334f7SCédric Le Goater * copies of the Software, and to permit persons to whom the Software is 127a2334f7SCédric Le Goater * furnished to do so, subject to the following conditions: 137a2334f7SCédric Le Goater * 147a2334f7SCédric Le Goater * The above copyright notice and this permission notice shall be included in 157a2334f7SCédric Le Goater * all copies or substantial portions of the Software. 167a2334f7SCédric Le Goater * 177a2334f7SCédric Le Goater * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 187a2334f7SCédric Le Goater * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 197a2334f7SCédric Le Goater * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 207a2334f7SCédric Le Goater * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 217a2334f7SCédric Le Goater * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 227a2334f7SCédric Le Goater * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 237a2334f7SCédric Le Goater * THE SOFTWARE. 247a2334f7SCédric Le Goater */ 257a2334f7SCédric Le Goater 267a2334f7SCédric Le Goater #include "qemu/osdep.h" 277a2334f7SCédric Le Goater #include "qemu/bswap.h" 28dd210749SThomas Huth #include "libqtest-single.h" 29188052a1SIris Chen #include "qemu/bitops.h" 307a2334f7SCédric Le Goater 317a2334f7SCédric Le Goater /* 327a2334f7SCédric Le Goater * ASPEED SPI Controller registers 337a2334f7SCédric Le Goater */ 347a2334f7SCédric Le Goater #define R_CONF 0x00 357a2334f7SCédric Le Goater #define CONF_ENABLE_W0 (1 << 16) 367a2334f7SCédric Le Goater #define R_CE_CTRL 0x04 377a2334f7SCédric Le Goater #define CRTL_EXTENDED0 0 /* 32 bit addressing for SPI */ 387a2334f7SCédric Le Goater #define R_CTRL0 0x10 397a2334f7SCédric Le Goater #define CTRL_CE_STOP_ACTIVE (1 << 2) 40371a3dd2SCédric Le Goater #define CTRL_READMODE 0x0 41371a3dd2SCédric Le Goater #define CTRL_FREADMODE 0x1 42371a3dd2SCédric Le Goater #define CTRL_WRITEMODE 0x2 437a2334f7SCédric Le Goater #define CTRL_USERMODE 0x3 44188052a1SIris Chen #define SR_WEL BIT(1) 457a2334f7SCédric Le Goater 467a2334f7SCédric Le Goater #define ASPEED_FMC_BASE 0x1E620000 477a2334f7SCédric Le Goater #define ASPEED_FLASH_BASE 0x20000000 487a2334f7SCédric Le Goater 497a2334f7SCédric Le Goater /* 507a2334f7SCédric Le Goater * Flash commands 517a2334f7SCédric Le Goater */ 527a2334f7SCédric Le Goater enum { 537a2334f7SCédric Le Goater JEDEC_READ = 0x9f, 54188052a1SIris Chen RDSR = 0x5, 55188052a1SIris Chen WRDI = 0x4, 567a2334f7SCédric Le Goater BULK_ERASE = 0xc7, 577a2334f7SCédric Le Goater READ = 0x03, 587a2334f7SCédric Le Goater PP = 0x02, 591de51272SIris Chen WRSR = 0x1, 607a2334f7SCédric Le Goater WREN = 0x6, 611de51272SIris Chen SRWD = 0x80, 62bd9f5052SCédric Le Goater RESET_ENABLE = 0x66, 63bd9f5052SCédric Le Goater RESET_MEMORY = 0x99, 647a2334f7SCédric Le Goater EN_4BYTE_ADDR = 0xB7, 657a2334f7SCédric Le Goater ERASE_SECTOR = 0xd8, 667a2334f7SCédric Le Goater }; 677a2334f7SCédric Le Goater 687a2334f7SCédric Le Goater #define FLASH_JEDEC 0x20ba19 /* n25q256a */ 697a2334f7SCédric Le Goater #define FLASH_SIZE (32 * 1024 * 1024) 707a2334f7SCédric Le Goater 71d2c4f384SJiaxun Yang #define FLASH_PAGE_SIZE 256 727a2334f7SCédric Le Goater 737a2334f7SCédric Le Goater /* 747a2334f7SCédric Le Goater * Use an explicit bswap for the values read/wrote to the flash region 757a2334f7SCédric Le Goater * as they are BE and the Aspeed CPU is LE. 767a2334f7SCédric Le Goater */ 777a2334f7SCédric Le Goater static inline uint32_t make_be32(uint32_t data) 787a2334f7SCédric Le Goater { 797a2334f7SCédric Le Goater return bswap32(data); 807a2334f7SCédric Le Goater } 817a2334f7SCédric Le Goater 827a2334f7SCédric Le Goater static void spi_conf(uint32_t value) 837a2334f7SCédric Le Goater { 847a2334f7SCédric Le Goater uint32_t conf = readl(ASPEED_FMC_BASE + R_CONF); 857a2334f7SCédric Le Goater 867a2334f7SCédric Le Goater conf |= value; 877a2334f7SCédric Le Goater writel(ASPEED_FMC_BASE + R_CONF, conf); 887a2334f7SCédric Le Goater } 897a2334f7SCédric Le Goater 90bd9f5052SCédric Le Goater static void spi_conf_remove(uint32_t value) 91bd9f5052SCédric Le Goater { 92bd9f5052SCédric Le Goater uint32_t conf = readl(ASPEED_FMC_BASE + R_CONF); 93bd9f5052SCédric Le Goater 94bd9f5052SCédric Le Goater conf &= ~value; 95bd9f5052SCédric Le Goater writel(ASPEED_FMC_BASE + R_CONF, conf); 96bd9f5052SCédric Le Goater } 97bd9f5052SCédric Le Goater 98371a3dd2SCédric Le Goater static void spi_ce_ctrl(uint32_t value) 99371a3dd2SCédric Le Goater { 100371a3dd2SCédric Le Goater uint32_t conf = readl(ASPEED_FMC_BASE + R_CE_CTRL); 101371a3dd2SCédric Le Goater 102371a3dd2SCédric Le Goater conf |= value; 103371a3dd2SCédric Le Goater writel(ASPEED_FMC_BASE + R_CE_CTRL, conf); 104371a3dd2SCédric Le Goater } 105371a3dd2SCédric Le Goater 106371a3dd2SCédric Le Goater static void spi_ctrl_setmode(uint8_t mode, uint8_t cmd) 107371a3dd2SCédric Le Goater { 108371a3dd2SCédric Le Goater uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); 109371a3dd2SCédric Le Goater ctrl &= ~(CTRL_USERMODE | 0xff << 16); 110371a3dd2SCédric Le Goater ctrl |= mode | (cmd << 16); 111371a3dd2SCédric Le Goater writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); 112371a3dd2SCédric Le Goater } 113371a3dd2SCédric Le Goater 1147a2334f7SCédric Le Goater static void spi_ctrl_start_user(void) 1157a2334f7SCédric Le Goater { 1167a2334f7SCédric Le Goater uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); 1177a2334f7SCédric Le Goater 1187a2334f7SCédric Le Goater ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; 1197a2334f7SCédric Le Goater writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); 1207a2334f7SCédric Le Goater 1217a2334f7SCédric Le Goater ctrl &= ~CTRL_CE_STOP_ACTIVE; 1227a2334f7SCédric Le Goater writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); 1237a2334f7SCédric Le Goater } 1247a2334f7SCédric Le Goater 1257a2334f7SCédric Le Goater static void spi_ctrl_stop_user(void) 1267a2334f7SCédric Le Goater { 1277a2334f7SCédric Le Goater uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); 1287a2334f7SCédric Le Goater 1297a2334f7SCédric Le Goater ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; 1307a2334f7SCédric Le Goater writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); 1317a2334f7SCédric Le Goater } 1327a2334f7SCédric Le Goater 133bd9f5052SCédric Le Goater static void flash_reset(void) 134bd9f5052SCédric Le Goater { 135bd9f5052SCédric Le Goater spi_conf(CONF_ENABLE_W0); 136bd9f5052SCédric Le Goater 137bd9f5052SCédric Le Goater spi_ctrl_start_user(); 138bd9f5052SCédric Le Goater writeb(ASPEED_FLASH_BASE, RESET_ENABLE); 139bd9f5052SCédric Le Goater writeb(ASPEED_FLASH_BASE, RESET_MEMORY); 14092a45bdeSIris Chen writeb(ASPEED_FLASH_BASE, WREN); 14192a45bdeSIris Chen writeb(ASPEED_FLASH_BASE, BULK_ERASE); 14292a45bdeSIris Chen writeb(ASPEED_FLASH_BASE, WRDI); 143bd9f5052SCédric Le Goater spi_ctrl_stop_user(); 144bd9f5052SCédric Le Goater 145bd9f5052SCédric Le Goater spi_conf_remove(CONF_ENABLE_W0); 146bd9f5052SCédric Le Goater } 147bd9f5052SCédric Le Goater 1487a2334f7SCédric Le Goater static void test_read_jedec(void) 1497a2334f7SCédric Le Goater { 1507a2334f7SCédric Le Goater uint32_t jedec = 0x0; 1517a2334f7SCédric Le Goater 1527a2334f7SCédric Le Goater spi_conf(CONF_ENABLE_W0); 1537a2334f7SCédric Le Goater 1547a2334f7SCédric Le Goater spi_ctrl_start_user(); 1557a2334f7SCédric Le Goater writeb(ASPEED_FLASH_BASE, JEDEC_READ); 1567a2334f7SCédric Le Goater jedec |= readb(ASPEED_FLASH_BASE) << 16; 1577a2334f7SCédric Le Goater jedec |= readb(ASPEED_FLASH_BASE) << 8; 1587a2334f7SCédric Le Goater jedec |= readb(ASPEED_FLASH_BASE); 1597a2334f7SCédric Le Goater spi_ctrl_stop_user(); 1607a2334f7SCédric Le Goater 161bd9f5052SCédric Le Goater flash_reset(); 162bd9f5052SCédric Le Goater 1637a2334f7SCédric Le Goater g_assert_cmphex(jedec, ==, FLASH_JEDEC); 1647a2334f7SCédric Le Goater } 1657a2334f7SCédric Le Goater 1667a2334f7SCédric Le Goater static void read_page(uint32_t addr, uint32_t *page) 1677a2334f7SCédric Le Goater { 1687a2334f7SCédric Le Goater int i; 1697a2334f7SCédric Le Goater 1707a2334f7SCédric Le Goater spi_ctrl_start_user(); 1717a2334f7SCédric Le Goater 1727a2334f7SCédric Le Goater writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); 1737a2334f7SCédric Le Goater writeb(ASPEED_FLASH_BASE, READ); 1747a2334f7SCédric Le Goater writel(ASPEED_FLASH_BASE, make_be32(addr)); 1757a2334f7SCédric Le Goater 1767a2334f7SCédric Le Goater /* Continuous read are supported */ 177d2c4f384SJiaxun Yang for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 1787a2334f7SCédric Le Goater page[i] = make_be32(readl(ASPEED_FLASH_BASE)); 1797a2334f7SCédric Le Goater } 1807a2334f7SCédric Le Goater spi_ctrl_stop_user(); 1817a2334f7SCédric Le Goater } 1827a2334f7SCédric Le Goater 183371a3dd2SCédric Le Goater static void read_page_mem(uint32_t addr, uint32_t *page) 184371a3dd2SCédric Le Goater { 185371a3dd2SCédric Le Goater int i; 186371a3dd2SCédric Le Goater 187371a3dd2SCédric Le Goater /* move out USER mode to use direct reads from the AHB bus */ 188371a3dd2SCédric Le Goater spi_ctrl_setmode(CTRL_READMODE, READ); 189371a3dd2SCédric Le Goater 190d2c4f384SJiaxun Yang for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 191371a3dd2SCédric Le Goater page[i] = make_be32(readl(ASPEED_FLASH_BASE + addr + i * 4)); 192371a3dd2SCédric Le Goater } 193371a3dd2SCédric Le Goater } 194371a3dd2SCédric Le Goater 1958abf9ba4SIris Chen static void write_page_mem(uint32_t addr, uint32_t write_value) 1968abf9ba4SIris Chen { 1978abf9ba4SIris Chen spi_ctrl_setmode(CTRL_WRITEMODE, PP); 1988abf9ba4SIris Chen 1998abf9ba4SIris Chen for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 2008abf9ba4SIris Chen writel(ASPEED_FLASH_BASE + addr + i * 4, write_value); 2018abf9ba4SIris Chen } 2028abf9ba4SIris Chen } 2038abf9ba4SIris Chen 2048abf9ba4SIris Chen static void assert_page_mem(uint32_t addr, uint32_t expected_value) 2058abf9ba4SIris Chen { 2068abf9ba4SIris Chen uint32_t page[FLASH_PAGE_SIZE / 4]; 2078abf9ba4SIris Chen read_page_mem(addr, page); 2088abf9ba4SIris Chen for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 2098abf9ba4SIris Chen g_assert_cmphex(page[i], ==, expected_value); 2108abf9ba4SIris Chen } 2118abf9ba4SIris Chen } 2128abf9ba4SIris Chen 2137a2334f7SCédric Le Goater static void test_erase_sector(void) 2147a2334f7SCédric Le Goater { 215d2c4f384SJiaxun Yang uint32_t some_page_addr = 0x600 * FLASH_PAGE_SIZE; 216d2c4f384SJiaxun Yang uint32_t page[FLASH_PAGE_SIZE / 4]; 2177a2334f7SCédric Le Goater int i; 2187a2334f7SCédric Le Goater 2197a2334f7SCédric Le Goater spi_conf(CONF_ENABLE_W0); 2207a2334f7SCédric Le Goater 22192a45bdeSIris Chen /* 22292a45bdeSIris Chen * Previous page should be full of 0xffs after backend is 22392a45bdeSIris Chen * initialized 22492a45bdeSIris Chen */ 22592a45bdeSIris Chen read_page(some_page_addr - FLASH_PAGE_SIZE, page); 22692a45bdeSIris Chen for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 22792a45bdeSIris Chen g_assert_cmphex(page[i], ==, 0xffffffff); 22892a45bdeSIris Chen } 22992a45bdeSIris Chen 23092a45bdeSIris Chen spi_ctrl_start_user(); 23192a45bdeSIris Chen writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); 23292a45bdeSIris Chen writeb(ASPEED_FLASH_BASE, WREN); 23392a45bdeSIris Chen writeb(ASPEED_FLASH_BASE, PP); 23492a45bdeSIris Chen writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); 23592a45bdeSIris Chen 23692a45bdeSIris Chen /* Fill the page with its own addresses */ 23792a45bdeSIris Chen for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 23892a45bdeSIris Chen writel(ASPEED_FLASH_BASE, make_be32(some_page_addr + i * 4)); 23992a45bdeSIris Chen } 24092a45bdeSIris Chen spi_ctrl_stop_user(); 24192a45bdeSIris Chen 24292a45bdeSIris Chen /* Check the page is correctly written */ 24392a45bdeSIris Chen read_page(some_page_addr, page); 24492a45bdeSIris Chen for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 24592a45bdeSIris Chen g_assert_cmphex(page[i], ==, some_page_addr + i * 4); 24692a45bdeSIris Chen } 24792a45bdeSIris Chen 2487a2334f7SCédric Le Goater spi_ctrl_start_user(); 2497a2334f7SCédric Le Goater writeb(ASPEED_FLASH_BASE, WREN); 2507a2334f7SCédric Le Goater writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); 2517a2334f7SCédric Le Goater writeb(ASPEED_FLASH_BASE, ERASE_SECTOR); 2527a2334f7SCédric Le Goater writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); 2537a2334f7SCédric Le Goater spi_ctrl_stop_user(); 2547a2334f7SCédric Le Goater 25592a45bdeSIris Chen /* Check the page is erased */ 2567a2334f7SCédric Le Goater read_page(some_page_addr, page); 257d2c4f384SJiaxun Yang for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 2587a2334f7SCédric Le Goater g_assert_cmphex(page[i], ==, 0xffffffff); 2597a2334f7SCédric Le Goater } 260bd9f5052SCédric Le Goater 261bd9f5052SCédric Le Goater flash_reset(); 2627a2334f7SCédric Le Goater } 2637a2334f7SCédric Le Goater 2647a2334f7SCédric Le Goater static void test_erase_all(void) 2657a2334f7SCédric Le Goater { 266d2c4f384SJiaxun Yang uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE; 267d2c4f384SJiaxun Yang uint32_t page[FLASH_PAGE_SIZE / 4]; 2687a2334f7SCédric Le Goater int i; 2697a2334f7SCédric Le Goater 2707a2334f7SCédric Le Goater spi_conf(CONF_ENABLE_W0); 2717a2334f7SCédric Le Goater 27292a45bdeSIris Chen /* 27392a45bdeSIris Chen * Previous page should be full of 0xffs after backend is 27492a45bdeSIris Chen * initialized 27592a45bdeSIris Chen */ 27692a45bdeSIris Chen read_page(some_page_addr - FLASH_PAGE_SIZE, page); 27792a45bdeSIris Chen for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 27892a45bdeSIris Chen g_assert_cmphex(page[i], ==, 0xffffffff); 27992a45bdeSIris Chen } 28092a45bdeSIris Chen 28192a45bdeSIris Chen spi_ctrl_start_user(); 28292a45bdeSIris Chen writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); 28392a45bdeSIris Chen writeb(ASPEED_FLASH_BASE, WREN); 28492a45bdeSIris Chen writeb(ASPEED_FLASH_BASE, PP); 28592a45bdeSIris Chen writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); 28692a45bdeSIris Chen 28792a45bdeSIris Chen /* Fill the page with its own addresses */ 28892a45bdeSIris Chen for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 28992a45bdeSIris Chen writel(ASPEED_FLASH_BASE, make_be32(some_page_addr + i * 4)); 29092a45bdeSIris Chen } 29192a45bdeSIris Chen spi_ctrl_stop_user(); 29292a45bdeSIris Chen 29392a45bdeSIris Chen /* Check the page is correctly written */ 2947a2334f7SCédric Le Goater read_page(some_page_addr, page); 295d2c4f384SJiaxun Yang for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 29692a45bdeSIris Chen g_assert_cmphex(page[i], ==, some_page_addr + i * 4); 2977a2334f7SCédric Le Goater } 2987a2334f7SCédric Le Goater 2997a2334f7SCédric Le Goater spi_ctrl_start_user(); 3007a2334f7SCédric Le Goater writeb(ASPEED_FLASH_BASE, WREN); 3017a2334f7SCédric Le Goater writeb(ASPEED_FLASH_BASE, BULK_ERASE); 3027a2334f7SCédric Le Goater spi_ctrl_stop_user(); 3037a2334f7SCédric Le Goater 30492a45bdeSIris Chen /* Check the page is erased */ 3057a2334f7SCédric Le Goater read_page(some_page_addr, page); 306d2c4f384SJiaxun Yang for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 3077a2334f7SCédric Le Goater g_assert_cmphex(page[i], ==, 0xffffffff); 3087a2334f7SCédric Le Goater } 309bd9f5052SCédric Le Goater 310bd9f5052SCédric Le Goater flash_reset(); 3117a2334f7SCédric Le Goater } 3127a2334f7SCédric Le Goater 3137a2334f7SCédric Le Goater static void test_write_page(void) 3147a2334f7SCédric Le Goater { 315d2c4f384SJiaxun Yang uint32_t my_page_addr = 0x14000 * FLASH_PAGE_SIZE; /* beyond 16MB */ 316d2c4f384SJiaxun Yang uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE; 317d2c4f384SJiaxun Yang uint32_t page[FLASH_PAGE_SIZE / 4]; 3187a2334f7SCédric Le Goater int i; 3197a2334f7SCédric Le Goater 3207a2334f7SCédric Le Goater spi_conf(CONF_ENABLE_W0); 3217a2334f7SCédric Le Goater 3227a2334f7SCédric Le Goater spi_ctrl_start_user(); 3237a2334f7SCédric Le Goater writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); 324bd9f5052SCédric Le Goater writeb(ASPEED_FLASH_BASE, WREN); 3257a2334f7SCédric Le Goater writeb(ASPEED_FLASH_BASE, PP); 3267a2334f7SCédric Le Goater writel(ASPEED_FLASH_BASE, make_be32(my_page_addr)); 3277a2334f7SCédric Le Goater 3287a2334f7SCédric Le Goater /* Fill the page with its own addresses */ 329d2c4f384SJiaxun Yang for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 3307a2334f7SCédric Le Goater writel(ASPEED_FLASH_BASE, make_be32(my_page_addr + i * 4)); 3317a2334f7SCédric Le Goater } 3327a2334f7SCédric Le Goater spi_ctrl_stop_user(); 3337a2334f7SCédric Le Goater 3347a2334f7SCédric Le Goater /* Check what was written */ 3357a2334f7SCédric Le Goater read_page(my_page_addr, page); 336d2c4f384SJiaxun Yang for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 3377a2334f7SCédric Le Goater g_assert_cmphex(page[i], ==, my_page_addr + i * 4); 3387a2334f7SCédric Le Goater } 3397a2334f7SCédric Le Goater 3407a2334f7SCédric Le Goater /* Check some other page. It should be full of 0xff */ 3417a2334f7SCédric Le Goater read_page(some_page_addr, page); 342d2c4f384SJiaxun Yang for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 3437a2334f7SCédric Le Goater g_assert_cmphex(page[i], ==, 0xffffffff); 3447a2334f7SCédric Le Goater } 345bd9f5052SCédric Le Goater 346bd9f5052SCédric Le Goater flash_reset(); 3477a2334f7SCédric Le Goater } 3487a2334f7SCédric Le Goater 349371a3dd2SCédric Le Goater static void test_read_page_mem(void) 350371a3dd2SCédric Le Goater { 351d2c4f384SJiaxun Yang uint32_t my_page_addr = 0x14000 * FLASH_PAGE_SIZE; /* beyond 16MB */ 352d2c4f384SJiaxun Yang uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE; 353d2c4f384SJiaxun Yang uint32_t page[FLASH_PAGE_SIZE / 4]; 354371a3dd2SCédric Le Goater int i; 355371a3dd2SCédric Le Goater 3561df52a9aSJamin Lin /* 3571df52a9aSJamin Lin * Enable 4BYTE mode for controller. This is should be strapped by 358371a3dd2SCédric Le Goater * HW for CE0 anyhow. 359371a3dd2SCédric Le Goater */ 360371a3dd2SCédric Le Goater spi_ce_ctrl(1 << CRTL_EXTENDED0); 361371a3dd2SCédric Le Goater 362371a3dd2SCédric Le Goater /* Enable 4BYTE mode for flash. */ 363371a3dd2SCédric Le Goater spi_conf(CONF_ENABLE_W0); 364371a3dd2SCédric Le Goater spi_ctrl_start_user(); 365371a3dd2SCédric Le Goater writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); 36692a45bdeSIris Chen writeb(ASPEED_FLASH_BASE, WREN); 36792a45bdeSIris Chen writeb(ASPEED_FLASH_BASE, PP); 36892a45bdeSIris Chen writel(ASPEED_FLASH_BASE, make_be32(my_page_addr)); 36992a45bdeSIris Chen 37092a45bdeSIris Chen /* Fill the page with its own addresses */ 37192a45bdeSIris Chen for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 37292a45bdeSIris Chen writel(ASPEED_FLASH_BASE, make_be32(my_page_addr + i * 4)); 37392a45bdeSIris Chen } 374371a3dd2SCédric Le Goater spi_ctrl_stop_user(); 375371a3dd2SCédric Le Goater spi_conf_remove(CONF_ENABLE_W0); 376371a3dd2SCédric Le Goater 377371a3dd2SCédric Le Goater /* Check what was written */ 378371a3dd2SCédric Le Goater read_page_mem(my_page_addr, page); 379d2c4f384SJiaxun Yang for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 380371a3dd2SCédric Le Goater g_assert_cmphex(page[i], ==, my_page_addr + i * 4); 381371a3dd2SCédric Le Goater } 382371a3dd2SCédric Le Goater 383371a3dd2SCédric Le Goater /* Check some other page. It should be full of 0xff */ 384371a3dd2SCédric Le Goater read_page_mem(some_page_addr, page); 385d2c4f384SJiaxun Yang for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 386371a3dd2SCédric Le Goater g_assert_cmphex(page[i], ==, 0xffffffff); 387371a3dd2SCédric Le Goater } 388371a3dd2SCédric Le Goater 389371a3dd2SCédric Le Goater flash_reset(); 390371a3dd2SCédric Le Goater } 391371a3dd2SCédric Le Goater 392371a3dd2SCédric Le Goater static void test_write_page_mem(void) 393371a3dd2SCédric Le Goater { 394d2c4f384SJiaxun Yang uint32_t my_page_addr = 0x15000 * FLASH_PAGE_SIZE; 395d2c4f384SJiaxun Yang uint32_t page[FLASH_PAGE_SIZE / 4]; 396371a3dd2SCédric Le Goater int i; 397371a3dd2SCédric Le Goater 3981df52a9aSJamin Lin /* 3991df52a9aSJamin Lin * Enable 4BYTE mode for controller. This is should be strapped by 400371a3dd2SCédric Le Goater * HW for CE0 anyhow. 401371a3dd2SCédric Le Goater */ 402371a3dd2SCédric Le Goater spi_ce_ctrl(1 << CRTL_EXTENDED0); 403371a3dd2SCédric Le Goater 404371a3dd2SCédric Le Goater /* Enable 4BYTE mode for flash. */ 405371a3dd2SCédric Le Goater spi_conf(CONF_ENABLE_W0); 406371a3dd2SCédric Le Goater spi_ctrl_start_user(); 407371a3dd2SCédric Le Goater writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); 408371a3dd2SCédric Le Goater writeb(ASPEED_FLASH_BASE, WREN); 409371a3dd2SCédric Le Goater spi_ctrl_stop_user(); 410371a3dd2SCédric Le Goater 411371a3dd2SCédric Le Goater /* move out USER mode to use direct writes to the AHB bus */ 412371a3dd2SCédric Le Goater spi_ctrl_setmode(CTRL_WRITEMODE, PP); 413371a3dd2SCédric Le Goater 414d2c4f384SJiaxun Yang for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 415371a3dd2SCédric Le Goater writel(ASPEED_FLASH_BASE + my_page_addr + i * 4, 416371a3dd2SCédric Le Goater make_be32(my_page_addr + i * 4)); 417371a3dd2SCédric Le Goater } 418371a3dd2SCédric Le Goater 419371a3dd2SCédric Le Goater /* Check what was written */ 420371a3dd2SCédric Le Goater read_page_mem(my_page_addr, page); 421d2c4f384SJiaxun Yang for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { 422371a3dd2SCédric Le Goater g_assert_cmphex(page[i], ==, my_page_addr + i * 4); 423371a3dd2SCédric Le Goater } 424371a3dd2SCédric Le Goater 425371a3dd2SCédric Le Goater flash_reset(); 426371a3dd2SCédric Le Goater } 427371a3dd2SCédric Le Goater 428188052a1SIris Chen static void test_read_status_reg(void) 429188052a1SIris Chen { 430188052a1SIris Chen uint8_t r; 431188052a1SIris Chen 432188052a1SIris Chen spi_conf(CONF_ENABLE_W0); 433188052a1SIris Chen 434188052a1SIris Chen spi_ctrl_start_user(); 435188052a1SIris Chen writeb(ASPEED_FLASH_BASE, RDSR); 436188052a1SIris Chen r = readb(ASPEED_FLASH_BASE); 437188052a1SIris Chen spi_ctrl_stop_user(); 438188052a1SIris Chen 439188052a1SIris Chen g_assert_cmphex(r & SR_WEL, ==, 0); 440188052a1SIris Chen g_assert(!qtest_qom_get_bool 441188052a1SIris Chen (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); 442188052a1SIris Chen 443188052a1SIris Chen spi_ctrl_start_user(); 444188052a1SIris Chen writeb(ASPEED_FLASH_BASE, WREN); 445188052a1SIris Chen writeb(ASPEED_FLASH_BASE, RDSR); 446188052a1SIris Chen r = readb(ASPEED_FLASH_BASE); 447188052a1SIris Chen spi_ctrl_stop_user(); 448188052a1SIris Chen 449188052a1SIris Chen g_assert_cmphex(r & SR_WEL, ==, SR_WEL); 450188052a1SIris Chen g_assert(qtest_qom_get_bool 451188052a1SIris Chen (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); 452188052a1SIris Chen 453188052a1SIris Chen spi_ctrl_start_user(); 454188052a1SIris Chen writeb(ASPEED_FLASH_BASE, WRDI); 455188052a1SIris Chen writeb(ASPEED_FLASH_BASE, RDSR); 456188052a1SIris Chen r = readb(ASPEED_FLASH_BASE); 457188052a1SIris Chen spi_ctrl_stop_user(); 458188052a1SIris Chen 459188052a1SIris Chen g_assert_cmphex(r & SR_WEL, ==, 0); 460188052a1SIris Chen g_assert(!qtest_qom_get_bool 461188052a1SIris Chen (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); 462188052a1SIris Chen 463188052a1SIris Chen flash_reset(); 464188052a1SIris Chen } 465188052a1SIris Chen 4661de51272SIris Chen static void test_status_reg_write_protection(void) 4671de51272SIris Chen { 4681de51272SIris Chen uint8_t r; 4691de51272SIris Chen 4701de51272SIris Chen spi_conf(CONF_ENABLE_W0); 4711de51272SIris Chen 4721de51272SIris Chen /* default case: WP# is high and SRWD is low -> status register writable */ 4731de51272SIris Chen spi_ctrl_start_user(); 4741de51272SIris Chen writeb(ASPEED_FLASH_BASE, WREN); 4751de51272SIris Chen /* test ability to write SRWD */ 4761de51272SIris Chen writeb(ASPEED_FLASH_BASE, WRSR); 4771de51272SIris Chen writeb(ASPEED_FLASH_BASE, SRWD); 4781de51272SIris Chen writeb(ASPEED_FLASH_BASE, RDSR); 4791de51272SIris Chen r = readb(ASPEED_FLASH_BASE); 4801de51272SIris Chen spi_ctrl_stop_user(); 4811de51272SIris Chen g_assert_cmphex(r & SRWD, ==, SRWD); 4821de51272SIris Chen 4831de51272SIris Chen /* WP# high and SRWD high -> status register writable */ 4841de51272SIris Chen spi_ctrl_start_user(); 4851de51272SIris Chen writeb(ASPEED_FLASH_BASE, WREN); 4861de51272SIris Chen /* test ability to write SRWD */ 4871de51272SIris Chen writeb(ASPEED_FLASH_BASE, WRSR); 4881de51272SIris Chen writeb(ASPEED_FLASH_BASE, 0); 4891de51272SIris Chen writeb(ASPEED_FLASH_BASE, RDSR); 4901de51272SIris Chen r = readb(ASPEED_FLASH_BASE); 4911de51272SIris Chen spi_ctrl_stop_user(); 4921de51272SIris Chen g_assert_cmphex(r & SRWD, ==, 0); 4931de51272SIris Chen 4941de51272SIris Chen /* WP# low and SRWD low -> status register writable */ 4951de51272SIris Chen qtest_set_irq_in(global_qtest, 4961de51272SIris Chen "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 0); 4971de51272SIris Chen spi_ctrl_start_user(); 4981de51272SIris Chen writeb(ASPEED_FLASH_BASE, WREN); 4991de51272SIris Chen /* test ability to write SRWD */ 5001de51272SIris Chen writeb(ASPEED_FLASH_BASE, WRSR); 5011de51272SIris Chen writeb(ASPEED_FLASH_BASE, SRWD); 5021de51272SIris Chen writeb(ASPEED_FLASH_BASE, RDSR); 5031de51272SIris Chen r = readb(ASPEED_FLASH_BASE); 5041de51272SIris Chen spi_ctrl_stop_user(); 5051de51272SIris Chen g_assert_cmphex(r & SRWD, ==, SRWD); 5061de51272SIris Chen 5071de51272SIris Chen /* WP# low and SRWD high -> status register NOT writable */ 5081de51272SIris Chen spi_ctrl_start_user(); 5091de51272SIris Chen writeb(ASPEED_FLASH_BASE, WREN); 5101de51272SIris Chen /* test ability to write SRWD */ 5111de51272SIris Chen writeb(ASPEED_FLASH_BASE, WRSR); 5121de51272SIris Chen writeb(ASPEED_FLASH_BASE, 0); 5131de51272SIris Chen writeb(ASPEED_FLASH_BASE, RDSR); 5141de51272SIris Chen r = readb(ASPEED_FLASH_BASE); 5151de51272SIris Chen spi_ctrl_stop_user(); 5161de51272SIris Chen /* write is not successful */ 5171de51272SIris Chen g_assert_cmphex(r & SRWD, ==, SRWD); 5181de51272SIris Chen 5191de51272SIris Chen qtest_set_irq_in(global_qtest, 5201de51272SIris Chen "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 1); 5211de51272SIris Chen flash_reset(); 5221de51272SIris Chen } 5231de51272SIris Chen 5248abf9ba4SIris Chen static void test_write_block_protect(void) 5258abf9ba4SIris Chen { 5268abf9ba4SIris Chen uint32_t sector_size = 65536; 5278abf9ba4SIris Chen uint32_t n_sectors = 512; 5288abf9ba4SIris Chen 5298abf9ba4SIris Chen spi_ce_ctrl(1 << CRTL_EXTENDED0); 5308abf9ba4SIris Chen spi_conf(CONF_ENABLE_W0); 5318abf9ba4SIris Chen 5328abf9ba4SIris Chen uint32_t bp_bits = 0b0; 5338abf9ba4SIris Chen 5348abf9ba4SIris Chen for (int i = 0; i < 16; i++) { 5358abf9ba4SIris Chen bp_bits = ((i & 0b1000) << 3) | ((i & 0b0111) << 2); 5368abf9ba4SIris Chen 5378abf9ba4SIris Chen spi_ctrl_start_user(); 5388abf9ba4SIris Chen writeb(ASPEED_FLASH_BASE, WREN); 5398abf9ba4SIris Chen writeb(ASPEED_FLASH_BASE, BULK_ERASE); 5408abf9ba4SIris Chen writeb(ASPEED_FLASH_BASE, WREN); 5418abf9ba4SIris Chen writeb(ASPEED_FLASH_BASE, WRSR); 5428abf9ba4SIris Chen writeb(ASPEED_FLASH_BASE, bp_bits); 5438abf9ba4SIris Chen writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); 5448abf9ba4SIris Chen writeb(ASPEED_FLASH_BASE, WREN); 5458abf9ba4SIris Chen spi_ctrl_stop_user(); 5468abf9ba4SIris Chen 5478abf9ba4SIris Chen uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; 5488abf9ba4SIris Chen uint32_t protection_start = n_sectors - num_protected_sectors; 5498abf9ba4SIris Chen uint32_t protection_end = n_sectors; 5508abf9ba4SIris Chen 5518abf9ba4SIris Chen for (int sector = 0; sector < n_sectors; sector++) { 5528abf9ba4SIris Chen uint32_t addr = sector * sector_size; 5538abf9ba4SIris Chen 5548abf9ba4SIris Chen assert_page_mem(addr, 0xffffffff); 5558abf9ba4SIris Chen write_page_mem(addr, make_be32(0xabcdef12)); 5568abf9ba4SIris Chen 5578abf9ba4SIris Chen uint32_t expected_value = protection_start <= sector 5588abf9ba4SIris Chen && sector < protection_end 5598abf9ba4SIris Chen ? 0xffffffff : 0xabcdef12; 5608abf9ba4SIris Chen 5618abf9ba4SIris Chen assert_page_mem(addr, expected_value); 5628abf9ba4SIris Chen } 5638abf9ba4SIris Chen } 5648abf9ba4SIris Chen 5658abf9ba4SIris Chen flash_reset(); 5668abf9ba4SIris Chen } 5678abf9ba4SIris Chen 5688abf9ba4SIris Chen static void test_write_block_protect_bottom_bit(void) 5698abf9ba4SIris Chen { 5708abf9ba4SIris Chen uint32_t sector_size = 65536; 5718abf9ba4SIris Chen uint32_t n_sectors = 512; 5728abf9ba4SIris Chen 5738abf9ba4SIris Chen spi_ce_ctrl(1 << CRTL_EXTENDED0); 5748abf9ba4SIris Chen spi_conf(CONF_ENABLE_W0); 5758abf9ba4SIris Chen 5768abf9ba4SIris Chen /* top bottom bit is enabled */ 5778abf9ba4SIris Chen uint32_t bp_bits = 0b00100 << 3; 5788abf9ba4SIris Chen 5798abf9ba4SIris Chen for (int i = 0; i < 16; i++) { 5808abf9ba4SIris Chen bp_bits = (((i & 0b1000) | 0b0100) << 3) | ((i & 0b0111) << 2); 5818abf9ba4SIris Chen 5828abf9ba4SIris Chen spi_ctrl_start_user(); 5838abf9ba4SIris Chen writeb(ASPEED_FLASH_BASE, WREN); 5848abf9ba4SIris Chen writeb(ASPEED_FLASH_BASE, BULK_ERASE); 5858abf9ba4SIris Chen writeb(ASPEED_FLASH_BASE, WREN); 5868abf9ba4SIris Chen writeb(ASPEED_FLASH_BASE, WRSR); 5878abf9ba4SIris Chen writeb(ASPEED_FLASH_BASE, bp_bits); 5888abf9ba4SIris Chen writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); 5898abf9ba4SIris Chen writeb(ASPEED_FLASH_BASE, WREN); 5908abf9ba4SIris Chen spi_ctrl_stop_user(); 5918abf9ba4SIris Chen 5928abf9ba4SIris Chen uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; 5938abf9ba4SIris Chen uint32_t protection_start = 0; 5948abf9ba4SIris Chen uint32_t protection_end = num_protected_sectors; 5958abf9ba4SIris Chen 5968abf9ba4SIris Chen for (int sector = 0; sector < n_sectors; sector++) { 5978abf9ba4SIris Chen uint32_t addr = sector * sector_size; 5988abf9ba4SIris Chen 5998abf9ba4SIris Chen assert_page_mem(addr, 0xffffffff); 6008abf9ba4SIris Chen write_page_mem(addr, make_be32(0xabcdef12)); 6018abf9ba4SIris Chen 6028abf9ba4SIris Chen uint32_t expected_value = protection_start <= sector 6038abf9ba4SIris Chen && sector < protection_end 6048abf9ba4SIris Chen ? 0xffffffff : 0xabcdef12; 6058abf9ba4SIris Chen 6068abf9ba4SIris Chen assert_page_mem(addr, expected_value); 6078abf9ba4SIris Chen } 6088abf9ba4SIris Chen } 6098abf9ba4SIris Chen 6108abf9ba4SIris Chen flash_reset(); 6118abf9ba4SIris Chen } 6128abf9ba4SIris Chen 613*755e984aSJamin Lin static int test_palmetto_bmc(void) 6147a2334f7SCédric Le Goater { 61539df79e4SBin Meng g_autofree char *tmp_path = NULL; 6167a2334f7SCédric Le Goater int ret; 6177a2334f7SCédric Le Goater int fd; 6187a2334f7SCédric Le Goater 61939df79e4SBin Meng fd = g_file_open_tmp("qtest.m25p80.XXXXXX", &tmp_path, NULL); 6207a2334f7SCédric Le Goater g_assert(fd >= 0); 6217a2334f7SCédric Le Goater ret = ftruncate(fd, FLASH_SIZE); 6227a2334f7SCédric Le Goater g_assert(ret == 0); 6237a2334f7SCédric Le Goater close(fd); 6247a2334f7SCédric Le Goater 62588b988c8SMarkus Armbruster global_qtest = qtest_initf("-m 256 -machine palmetto-bmc " 6267a2334f7SCédric Le Goater "-drive file=%s,format=raw,if=mtd", 6277a2334f7SCédric Le Goater tmp_path); 6287a2334f7SCédric Le Goater 6295fde7f10SCédric Le Goater qtest_add_func("/ast2400/smc/read_jedec", test_read_jedec); 6305fde7f10SCédric Le Goater qtest_add_func("/ast2400/smc/erase_sector", test_erase_sector); 6315fde7f10SCédric Le Goater qtest_add_func("/ast2400/smc/erase_all", test_erase_all); 6325fde7f10SCédric Le Goater qtest_add_func("/ast2400/smc/write_page", test_write_page); 6335fde7f10SCédric Le Goater qtest_add_func("/ast2400/smc/read_page_mem", test_read_page_mem); 6345fde7f10SCédric Le Goater qtest_add_func("/ast2400/smc/write_page_mem", test_write_page_mem); 635188052a1SIris Chen qtest_add_func("/ast2400/smc/read_status_reg", test_read_status_reg); 6361de51272SIris Chen qtest_add_func("/ast2400/smc/status_reg_write_protection", 6371de51272SIris Chen test_status_reg_write_protection); 6388abf9ba4SIris Chen qtest_add_func("/ast2400/smc/write_block_protect", 6398abf9ba4SIris Chen test_write_block_protect); 6408abf9ba4SIris Chen qtest_add_func("/ast2400/smc/write_block_protect_bottom_bit", 6418abf9ba4SIris Chen test_write_block_protect_bottom_bit); 6427a2334f7SCédric Le Goater 64392a45bdeSIris Chen flash_reset(); 6447a2334f7SCédric Le Goater ret = g_test_run(); 6457a2334f7SCédric Le Goater qtest_quit(global_qtest); 6467a2334f7SCédric Le Goater unlink(tmp_path); 647*755e984aSJamin Lin 648*755e984aSJamin Lin return ret; 649*755e984aSJamin Lin } 650*755e984aSJamin Lin 651*755e984aSJamin Lin int main(int argc, char **argv) 652*755e984aSJamin Lin { 653*755e984aSJamin Lin int ret; 654*755e984aSJamin Lin 655*755e984aSJamin Lin g_test_init(&argc, &argv, NULL); 656*755e984aSJamin Lin ret = test_palmetto_bmc(); 657*755e984aSJamin Lin 6587a2334f7SCédric Le Goater return ret; 6597a2334f7SCédric Le Goater } 660