xref: /qemu/tests/qtest/aspeed_smc-test.c (revision 1de51272bf7f71ccf4b1503f5b1018ca6d429675)
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,
59*1de51272SIris Chen     WRSR = 0x1,
607a2334f7SCédric Le Goater     WREN = 0x6,
61*1de51272SIris 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 
1957a2334f7SCédric Le Goater static void test_erase_sector(void)
1967a2334f7SCédric Le Goater {
197d2c4f384SJiaxun Yang     uint32_t some_page_addr = 0x600 * FLASH_PAGE_SIZE;
198d2c4f384SJiaxun Yang     uint32_t page[FLASH_PAGE_SIZE / 4];
1997a2334f7SCédric Le Goater     int i;
2007a2334f7SCédric Le Goater 
2017a2334f7SCédric Le Goater     spi_conf(CONF_ENABLE_W0);
2027a2334f7SCédric Le Goater 
20392a45bdeSIris Chen     /*
20492a45bdeSIris Chen      * Previous page should be full of 0xffs after backend is
20592a45bdeSIris Chen      * initialized
20692a45bdeSIris Chen      */
20792a45bdeSIris Chen     read_page(some_page_addr - FLASH_PAGE_SIZE, page);
20892a45bdeSIris Chen     for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
20992a45bdeSIris Chen         g_assert_cmphex(page[i], ==, 0xffffffff);
21092a45bdeSIris Chen     }
21192a45bdeSIris Chen 
21292a45bdeSIris Chen     spi_ctrl_start_user();
21392a45bdeSIris Chen     writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
21492a45bdeSIris Chen     writeb(ASPEED_FLASH_BASE, WREN);
21592a45bdeSIris Chen     writeb(ASPEED_FLASH_BASE, PP);
21692a45bdeSIris Chen     writel(ASPEED_FLASH_BASE, make_be32(some_page_addr));
21792a45bdeSIris Chen 
21892a45bdeSIris Chen     /* Fill the page with its own addresses */
21992a45bdeSIris Chen     for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
22092a45bdeSIris Chen         writel(ASPEED_FLASH_BASE, make_be32(some_page_addr + i * 4));
22192a45bdeSIris Chen     }
22292a45bdeSIris Chen     spi_ctrl_stop_user();
22392a45bdeSIris Chen 
22492a45bdeSIris Chen     /* Check the page is correctly written */
22592a45bdeSIris Chen     read_page(some_page_addr, page);
22692a45bdeSIris Chen     for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
22792a45bdeSIris Chen         g_assert_cmphex(page[i], ==, some_page_addr + i * 4);
22892a45bdeSIris Chen     }
22992a45bdeSIris Chen 
2307a2334f7SCédric Le Goater     spi_ctrl_start_user();
2317a2334f7SCédric Le Goater     writeb(ASPEED_FLASH_BASE, WREN);
2327a2334f7SCédric Le Goater     writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
2337a2334f7SCédric Le Goater     writeb(ASPEED_FLASH_BASE, ERASE_SECTOR);
2347a2334f7SCédric Le Goater     writel(ASPEED_FLASH_BASE, make_be32(some_page_addr));
2357a2334f7SCédric Le Goater     spi_ctrl_stop_user();
2367a2334f7SCédric Le Goater 
23792a45bdeSIris Chen     /* Check the page is erased */
2387a2334f7SCédric Le Goater     read_page(some_page_addr, page);
239d2c4f384SJiaxun Yang     for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
2407a2334f7SCédric Le Goater         g_assert_cmphex(page[i], ==, 0xffffffff);
2417a2334f7SCédric Le Goater     }
242bd9f5052SCédric Le Goater 
243bd9f5052SCédric Le Goater     flash_reset();
2447a2334f7SCédric Le Goater }
2457a2334f7SCédric Le Goater 
2467a2334f7SCédric Le Goater static void test_erase_all(void)
2477a2334f7SCédric Le Goater {
248d2c4f384SJiaxun Yang     uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE;
249d2c4f384SJiaxun Yang     uint32_t page[FLASH_PAGE_SIZE / 4];
2507a2334f7SCédric Le Goater     int i;
2517a2334f7SCédric Le Goater 
2527a2334f7SCédric Le Goater     spi_conf(CONF_ENABLE_W0);
2537a2334f7SCédric Le Goater 
25492a45bdeSIris Chen     /*
25592a45bdeSIris Chen      * Previous page should be full of 0xffs after backend is
25692a45bdeSIris Chen      * initialized
25792a45bdeSIris Chen      */
25892a45bdeSIris Chen     read_page(some_page_addr - FLASH_PAGE_SIZE, page);
25992a45bdeSIris Chen     for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
26092a45bdeSIris Chen         g_assert_cmphex(page[i], ==, 0xffffffff);
26192a45bdeSIris Chen     }
26292a45bdeSIris Chen 
26392a45bdeSIris Chen     spi_ctrl_start_user();
26492a45bdeSIris Chen     writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
26592a45bdeSIris Chen     writeb(ASPEED_FLASH_BASE, WREN);
26692a45bdeSIris Chen     writeb(ASPEED_FLASH_BASE, PP);
26792a45bdeSIris Chen     writel(ASPEED_FLASH_BASE, make_be32(some_page_addr));
26892a45bdeSIris Chen 
26992a45bdeSIris Chen     /* Fill the page with its own addresses */
27092a45bdeSIris Chen     for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
27192a45bdeSIris Chen         writel(ASPEED_FLASH_BASE, make_be32(some_page_addr + i * 4));
27292a45bdeSIris Chen     }
27392a45bdeSIris Chen     spi_ctrl_stop_user();
27492a45bdeSIris Chen 
27592a45bdeSIris Chen     /* Check the page is correctly written */
2767a2334f7SCédric Le Goater     read_page(some_page_addr, page);
277d2c4f384SJiaxun Yang     for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
27892a45bdeSIris Chen         g_assert_cmphex(page[i], ==, some_page_addr + i * 4);
2797a2334f7SCédric Le Goater     }
2807a2334f7SCédric Le Goater 
2817a2334f7SCédric Le Goater     spi_ctrl_start_user();
2827a2334f7SCédric Le Goater     writeb(ASPEED_FLASH_BASE, WREN);
2837a2334f7SCédric Le Goater     writeb(ASPEED_FLASH_BASE, BULK_ERASE);
2847a2334f7SCédric Le Goater     spi_ctrl_stop_user();
2857a2334f7SCédric Le Goater 
28692a45bdeSIris Chen     /* Check the page is erased */
2877a2334f7SCédric Le Goater     read_page(some_page_addr, page);
288d2c4f384SJiaxun Yang     for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
2897a2334f7SCédric Le Goater         g_assert_cmphex(page[i], ==, 0xffffffff);
2907a2334f7SCédric Le Goater     }
291bd9f5052SCédric Le Goater 
292bd9f5052SCédric Le Goater     flash_reset();
2937a2334f7SCédric Le Goater }
2947a2334f7SCédric Le Goater 
2957a2334f7SCédric Le Goater static void test_write_page(void)
2967a2334f7SCédric Le Goater {
297d2c4f384SJiaxun Yang     uint32_t my_page_addr = 0x14000 * FLASH_PAGE_SIZE; /* beyond 16MB */
298d2c4f384SJiaxun Yang     uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE;
299d2c4f384SJiaxun Yang     uint32_t page[FLASH_PAGE_SIZE / 4];
3007a2334f7SCédric Le Goater     int i;
3017a2334f7SCédric Le Goater 
3027a2334f7SCédric Le Goater     spi_conf(CONF_ENABLE_W0);
3037a2334f7SCédric Le Goater 
3047a2334f7SCédric Le Goater     spi_ctrl_start_user();
3057a2334f7SCédric Le Goater     writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
306bd9f5052SCédric Le Goater     writeb(ASPEED_FLASH_BASE, WREN);
3077a2334f7SCédric Le Goater     writeb(ASPEED_FLASH_BASE, PP);
3087a2334f7SCédric Le Goater     writel(ASPEED_FLASH_BASE, make_be32(my_page_addr));
3097a2334f7SCédric Le Goater 
3107a2334f7SCédric Le Goater     /* Fill the page with its own addresses */
311d2c4f384SJiaxun Yang     for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
3127a2334f7SCédric Le Goater         writel(ASPEED_FLASH_BASE, make_be32(my_page_addr + i * 4));
3137a2334f7SCédric Le Goater     }
3147a2334f7SCédric Le Goater     spi_ctrl_stop_user();
3157a2334f7SCédric Le Goater 
3167a2334f7SCédric Le Goater     /* Check what was written */
3177a2334f7SCédric Le Goater     read_page(my_page_addr, page);
318d2c4f384SJiaxun Yang     for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
3197a2334f7SCédric Le Goater         g_assert_cmphex(page[i], ==, my_page_addr + i * 4);
3207a2334f7SCédric Le Goater     }
3217a2334f7SCédric Le Goater 
3227a2334f7SCédric Le Goater     /* Check some other page. It should be full of 0xff */
3237a2334f7SCédric Le Goater     read_page(some_page_addr, page);
324d2c4f384SJiaxun Yang     for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
3257a2334f7SCédric Le Goater         g_assert_cmphex(page[i], ==, 0xffffffff);
3267a2334f7SCédric Le Goater     }
327bd9f5052SCédric Le Goater 
328bd9f5052SCédric Le Goater     flash_reset();
3297a2334f7SCédric Le Goater }
3307a2334f7SCédric Le Goater 
331371a3dd2SCédric Le Goater static void test_read_page_mem(void)
332371a3dd2SCédric Le Goater {
333d2c4f384SJiaxun Yang     uint32_t my_page_addr = 0x14000 * FLASH_PAGE_SIZE; /* beyond 16MB */
334d2c4f384SJiaxun Yang     uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE;
335d2c4f384SJiaxun Yang     uint32_t page[FLASH_PAGE_SIZE / 4];
336371a3dd2SCédric Le Goater     int i;
337371a3dd2SCédric Le Goater 
338371a3dd2SCédric Le Goater     /* Enable 4BYTE mode for controller. This is should be strapped by
339371a3dd2SCédric Le Goater      * HW for CE0 anyhow.
340371a3dd2SCédric Le Goater      */
341371a3dd2SCédric Le Goater     spi_ce_ctrl(1 << CRTL_EXTENDED0);
342371a3dd2SCédric Le Goater 
343371a3dd2SCédric Le Goater     /* Enable 4BYTE mode for flash. */
344371a3dd2SCédric Le Goater     spi_conf(CONF_ENABLE_W0);
345371a3dd2SCédric Le Goater     spi_ctrl_start_user();
346371a3dd2SCédric Le Goater     writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
34792a45bdeSIris Chen     writeb(ASPEED_FLASH_BASE, WREN);
34892a45bdeSIris Chen     writeb(ASPEED_FLASH_BASE, PP);
34992a45bdeSIris Chen     writel(ASPEED_FLASH_BASE, make_be32(my_page_addr));
35092a45bdeSIris Chen 
35192a45bdeSIris Chen     /* Fill the page with its own addresses */
35292a45bdeSIris Chen     for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
35392a45bdeSIris Chen         writel(ASPEED_FLASH_BASE, make_be32(my_page_addr + i * 4));
35492a45bdeSIris Chen     }
355371a3dd2SCédric Le Goater     spi_ctrl_stop_user();
356371a3dd2SCédric Le Goater     spi_conf_remove(CONF_ENABLE_W0);
357371a3dd2SCédric Le Goater 
358371a3dd2SCédric Le Goater     /* Check what was written */
359371a3dd2SCédric Le Goater     read_page_mem(my_page_addr, page);
360d2c4f384SJiaxun Yang     for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
361371a3dd2SCédric Le Goater         g_assert_cmphex(page[i], ==, my_page_addr + i * 4);
362371a3dd2SCédric Le Goater     }
363371a3dd2SCédric Le Goater 
364371a3dd2SCédric Le Goater     /* Check some other page. It should be full of 0xff */
365371a3dd2SCédric Le Goater     read_page_mem(some_page_addr, page);
366d2c4f384SJiaxun Yang     for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
367371a3dd2SCédric Le Goater         g_assert_cmphex(page[i], ==, 0xffffffff);
368371a3dd2SCédric Le Goater     }
369371a3dd2SCédric Le Goater 
370371a3dd2SCédric Le Goater     flash_reset();
371371a3dd2SCédric Le Goater }
372371a3dd2SCédric Le Goater 
373371a3dd2SCédric Le Goater static void test_write_page_mem(void)
374371a3dd2SCédric Le Goater {
375d2c4f384SJiaxun Yang     uint32_t my_page_addr = 0x15000 * FLASH_PAGE_SIZE;
376d2c4f384SJiaxun Yang     uint32_t page[FLASH_PAGE_SIZE / 4];
377371a3dd2SCédric Le Goater     int i;
378371a3dd2SCédric Le Goater 
379371a3dd2SCédric Le Goater     /* Enable 4BYTE mode for controller. This is should be strapped by
380371a3dd2SCédric Le Goater      * HW for CE0 anyhow.
381371a3dd2SCédric Le Goater      */
382371a3dd2SCédric Le Goater     spi_ce_ctrl(1 << CRTL_EXTENDED0);
383371a3dd2SCédric Le Goater 
384371a3dd2SCédric Le Goater     /* Enable 4BYTE mode for flash. */
385371a3dd2SCédric Le Goater     spi_conf(CONF_ENABLE_W0);
386371a3dd2SCédric Le Goater     spi_ctrl_start_user();
387371a3dd2SCédric Le Goater     writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
388371a3dd2SCédric Le Goater     writeb(ASPEED_FLASH_BASE, WREN);
389371a3dd2SCédric Le Goater     spi_ctrl_stop_user();
390371a3dd2SCédric Le Goater 
391371a3dd2SCédric Le Goater     /* move out USER mode to use direct writes to the AHB bus */
392371a3dd2SCédric Le Goater     spi_ctrl_setmode(CTRL_WRITEMODE, PP);
393371a3dd2SCédric Le Goater 
394d2c4f384SJiaxun Yang     for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
395371a3dd2SCédric Le Goater         writel(ASPEED_FLASH_BASE + my_page_addr + i * 4,
396371a3dd2SCédric Le Goater                make_be32(my_page_addr + i * 4));
397371a3dd2SCédric Le Goater     }
398371a3dd2SCédric Le Goater 
399371a3dd2SCédric Le Goater     /* Check what was written */
400371a3dd2SCédric Le Goater     read_page_mem(my_page_addr, page);
401d2c4f384SJiaxun Yang     for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) {
402371a3dd2SCédric Le Goater         g_assert_cmphex(page[i], ==, my_page_addr + i * 4);
403371a3dd2SCédric Le Goater     }
404371a3dd2SCédric Le Goater 
405371a3dd2SCédric Le Goater     flash_reset();
406371a3dd2SCédric Le Goater }
407371a3dd2SCédric Le Goater 
408188052a1SIris Chen static void test_read_status_reg(void)
409188052a1SIris Chen {
410188052a1SIris Chen     uint8_t r;
411188052a1SIris Chen 
412188052a1SIris Chen     spi_conf(CONF_ENABLE_W0);
413188052a1SIris Chen 
414188052a1SIris Chen     spi_ctrl_start_user();
415188052a1SIris Chen     writeb(ASPEED_FLASH_BASE, RDSR);
416188052a1SIris Chen     r = readb(ASPEED_FLASH_BASE);
417188052a1SIris Chen     spi_ctrl_stop_user();
418188052a1SIris Chen 
419188052a1SIris Chen     g_assert_cmphex(r & SR_WEL, ==, 0);
420188052a1SIris Chen     g_assert(!qtest_qom_get_bool
421188052a1SIris Chen             (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable"));
422188052a1SIris Chen 
423188052a1SIris Chen     spi_ctrl_start_user();
424188052a1SIris Chen     writeb(ASPEED_FLASH_BASE, WREN);
425188052a1SIris Chen     writeb(ASPEED_FLASH_BASE, RDSR);
426188052a1SIris Chen     r = readb(ASPEED_FLASH_BASE);
427188052a1SIris Chen     spi_ctrl_stop_user();
428188052a1SIris Chen 
429188052a1SIris Chen     g_assert_cmphex(r & SR_WEL, ==, SR_WEL);
430188052a1SIris Chen     g_assert(qtest_qom_get_bool
431188052a1SIris Chen             (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable"));
432188052a1SIris Chen 
433188052a1SIris Chen     spi_ctrl_start_user();
434188052a1SIris Chen     writeb(ASPEED_FLASH_BASE, WRDI);
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     flash_reset();
444188052a1SIris Chen }
445188052a1SIris Chen 
446*1de51272SIris Chen static void test_status_reg_write_protection(void)
447*1de51272SIris Chen {
448*1de51272SIris Chen     uint8_t r;
449*1de51272SIris Chen 
450*1de51272SIris Chen     spi_conf(CONF_ENABLE_W0);
451*1de51272SIris Chen 
452*1de51272SIris Chen     /* default case: WP# is high and SRWD is low -> status register writable */
453*1de51272SIris Chen     spi_ctrl_start_user();
454*1de51272SIris Chen     writeb(ASPEED_FLASH_BASE, WREN);
455*1de51272SIris Chen     /* test ability to write SRWD */
456*1de51272SIris Chen     writeb(ASPEED_FLASH_BASE, WRSR);
457*1de51272SIris Chen     writeb(ASPEED_FLASH_BASE, SRWD);
458*1de51272SIris Chen     writeb(ASPEED_FLASH_BASE, RDSR);
459*1de51272SIris Chen     r = readb(ASPEED_FLASH_BASE);
460*1de51272SIris Chen     spi_ctrl_stop_user();
461*1de51272SIris Chen     g_assert_cmphex(r & SRWD, ==, SRWD);
462*1de51272SIris Chen 
463*1de51272SIris Chen     /* WP# high and SRWD high -> status register writable */
464*1de51272SIris Chen     spi_ctrl_start_user();
465*1de51272SIris Chen     writeb(ASPEED_FLASH_BASE, WREN);
466*1de51272SIris Chen     /* test ability to write SRWD */
467*1de51272SIris Chen     writeb(ASPEED_FLASH_BASE, WRSR);
468*1de51272SIris Chen     writeb(ASPEED_FLASH_BASE, 0);
469*1de51272SIris Chen     writeb(ASPEED_FLASH_BASE, RDSR);
470*1de51272SIris Chen     r = readb(ASPEED_FLASH_BASE);
471*1de51272SIris Chen     spi_ctrl_stop_user();
472*1de51272SIris Chen     g_assert_cmphex(r & SRWD, ==, 0);
473*1de51272SIris Chen 
474*1de51272SIris Chen     /* WP# low and SRWD low -> status register writable */
475*1de51272SIris Chen     qtest_set_irq_in(global_qtest,
476*1de51272SIris Chen                      "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 0);
477*1de51272SIris Chen     spi_ctrl_start_user();
478*1de51272SIris Chen     writeb(ASPEED_FLASH_BASE, WREN);
479*1de51272SIris Chen     /* test ability to write SRWD */
480*1de51272SIris Chen     writeb(ASPEED_FLASH_BASE, WRSR);
481*1de51272SIris Chen     writeb(ASPEED_FLASH_BASE, SRWD);
482*1de51272SIris Chen     writeb(ASPEED_FLASH_BASE, RDSR);
483*1de51272SIris Chen     r = readb(ASPEED_FLASH_BASE);
484*1de51272SIris Chen     spi_ctrl_stop_user();
485*1de51272SIris Chen     g_assert_cmphex(r & SRWD, ==, SRWD);
486*1de51272SIris Chen 
487*1de51272SIris Chen     /* WP# low and SRWD high -> status register NOT writable */
488*1de51272SIris Chen     spi_ctrl_start_user();
489*1de51272SIris Chen     writeb(ASPEED_FLASH_BASE, WREN);
490*1de51272SIris Chen     /* test ability to write SRWD */
491*1de51272SIris Chen     writeb(ASPEED_FLASH_BASE, WRSR);
492*1de51272SIris Chen     writeb(ASPEED_FLASH_BASE, 0);
493*1de51272SIris Chen     writeb(ASPEED_FLASH_BASE, RDSR);
494*1de51272SIris Chen     r = readb(ASPEED_FLASH_BASE);
495*1de51272SIris Chen     spi_ctrl_stop_user();
496*1de51272SIris Chen     /* write is not successful */
497*1de51272SIris Chen     g_assert_cmphex(r & SRWD, ==, SRWD);
498*1de51272SIris Chen 
499*1de51272SIris Chen     qtest_set_irq_in(global_qtest,
500*1de51272SIris Chen                      "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 1);
501*1de51272SIris Chen     flash_reset();
502*1de51272SIris Chen }
503*1de51272SIris Chen 
5047a2334f7SCédric Le Goater static char tmp_path[] = "/tmp/qtest.m25p80.XXXXXX";
5057a2334f7SCédric Le Goater 
5067a2334f7SCédric Le Goater int main(int argc, char **argv)
5077a2334f7SCédric Le Goater {
5087a2334f7SCédric Le Goater     int ret;
5097a2334f7SCédric Le Goater     int fd;
5107a2334f7SCédric Le Goater 
5117a2334f7SCédric Le Goater     g_test_init(&argc, &argv, NULL);
5127a2334f7SCédric Le Goater 
5137a2334f7SCédric Le Goater     fd = mkstemp(tmp_path);
5147a2334f7SCédric Le Goater     g_assert(fd >= 0);
5157a2334f7SCédric Le Goater     ret = ftruncate(fd, FLASH_SIZE);
5167a2334f7SCédric Le Goater     g_assert(ret == 0);
5177a2334f7SCédric Le Goater     close(fd);
5187a2334f7SCédric Le Goater 
51988b988c8SMarkus Armbruster     global_qtest = qtest_initf("-m 256 -machine palmetto-bmc "
5207a2334f7SCédric Le Goater                                "-drive file=%s,format=raw,if=mtd",
5217a2334f7SCédric Le Goater                                tmp_path);
5227a2334f7SCédric Le Goater 
5235fde7f10SCédric Le Goater     qtest_add_func("/ast2400/smc/read_jedec", test_read_jedec);
5245fde7f10SCédric Le Goater     qtest_add_func("/ast2400/smc/erase_sector", test_erase_sector);
5255fde7f10SCédric Le Goater     qtest_add_func("/ast2400/smc/erase_all",  test_erase_all);
5265fde7f10SCédric Le Goater     qtest_add_func("/ast2400/smc/write_page", test_write_page);
5275fde7f10SCédric Le Goater     qtest_add_func("/ast2400/smc/read_page_mem", test_read_page_mem);
5285fde7f10SCédric Le Goater     qtest_add_func("/ast2400/smc/write_page_mem", test_write_page_mem);
529188052a1SIris Chen     qtest_add_func("/ast2400/smc/read_status_reg", test_read_status_reg);
530*1de51272SIris Chen     qtest_add_func("/ast2400/smc/status_reg_write_protection",
531*1de51272SIris Chen                    test_status_reg_write_protection);
5327a2334f7SCédric Le Goater 
53392a45bdeSIris Chen     flash_reset();
5347a2334f7SCédric Le Goater     ret = g_test_run();
5357a2334f7SCédric Le Goater 
5367a2334f7SCédric Le Goater     qtest_quit(global_qtest);
5377a2334f7SCédric Le Goater     unlink(tmp_path);
5387a2334f7SCédric Le Goater     return ret;
5397a2334f7SCédric Le Goater }
540