xref: /qemu/tests/qtest/aspeed_smc-test.c (revision 755e984aa4af97070a3f63812e9ed33f729dda9b)
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