aboutsummaryrefslogtreecommitdiff
path: root/tests/pflash-cfi02-test.c
diff options
context:
space:
mode:
authorStephen Checkoway <stephen.checkoway@oberlin.edu>2019-04-26 12:26:23 -0400
committerPhilippe Mathieu-Daudé <philmd@redhat.com>2019-07-02 02:34:55 +0200
commitddb6f2254871c7c686561c4b41d52e2f0413f9a1 (patch)
tree29620160fef0897eff2cbeb656d88e199380d12c /tests/pflash-cfi02-test.c
parenta50547aca54c0e55122c32c077cab747147a6b30 (diff)
hw/block/pflash_cfi02: Implement erase suspend/resume
During a sector erase (but not a chip erase), the embeded erase program can be suspended. Once suspended, the sectors not selected for erasure may be read and programmed. Autoselect mode is allowed during erase suspend mode. Presumably, CFI queries are similarly allowed so this commit allows them as well. Since guest firmware can use status bits DQ7, DQ6, DQ3, and DQ2 to determine the current state of sector erasure, these bits are properly implemented. Signed-off-by: Stephen Checkoway <stephen.checkoway@oberlin.edu> Message-Id: <20190426162624.55977-10-stephen.checkoway@oberlin.edu> Acked-by: Thomas Huth <thuth@redhat.com> Acked-by: Philippe Mathieu-Daudé <philmd@redhat.com> Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com> [PMD: Rebased] Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Diffstat (limited to 'tests/pflash-cfi02-test.c')
-rw-r--r--tests/pflash-cfi02-test.c110
1 files changed, 110 insertions, 0 deletions
diff --git a/tests/pflash-cfi02-test.c b/tests/pflash-cfi02-test.c
index 303bc87820..d3b23f4f66 100644
--- a/tests/pflash-cfi02-test.c
+++ b/tests/pflash-cfi02-test.c
@@ -43,6 +43,8 @@ typedef struct {
#define CHIP_ERASE_CMD 0x10
#define UNLOCK_BYPASS_CMD 0x20
#define UNLOCK_BYPASS_RESET_CMD 0x00
+#define ERASE_SUSPEND_CMD 0xB0
+#define ERASE_RESUME_CMD SECTOR_ERASE_CMD
typedef struct {
int bank_width;
@@ -241,6 +243,16 @@ static void chip_erase(const FlashConfig *c)
flash_cmd(c, UNLOCK0_ADDR, CHIP_ERASE_CMD);
}
+static void erase_suspend(const FlashConfig *c)
+{
+ flash_cmd(c, FLASH_ADDR(0), ERASE_SUSPEND_CMD);
+}
+
+static void erase_resume(const FlashConfig *c)
+{
+ flash_cmd(c, FLASH_ADDR(0), ERASE_RESUME_CMD);
+}
+
/*
* Test flash commands with a variety of device geometry.
*/
@@ -312,11 +324,20 @@ static void test_geometry(const void *opaque)
uint32_t device_len = 1 << flash_query_1(c, FLASH_ADDR(0x27));
g_assert_cmphex(device_len, ==, UNIFORM_FLASH_SIZE);
+ /* Check that erase suspend to read/write is supported. */
+ uint16_t pri = flash_query_1(c, FLASH_ADDR(0x15)) +
+ (flash_query_1(c, FLASH_ADDR(0x16)) << 8);
+ g_assert_cmpint(pri, >=, 0x2D + 4 * nb_erase_regions);
+ g_assert_cmpint(flash_query(c, FLASH_ADDR(pri + 0)), ==, replicate(c, 'P'));
+ g_assert_cmpint(flash_query(c, FLASH_ADDR(pri + 1)), ==, replicate(c, 'R'));
+ g_assert_cmpint(flash_query(c, FLASH_ADDR(pri + 2)), ==, replicate(c, 'I'));
+ g_assert_cmpint(flash_query_1(c, FLASH_ADDR(pri + 6)), ==, 2); /* R/W */
reset(c);
const uint64_t dq7 = replicate(c, 0x80);
const uint64_t dq6 = replicate(c, 0x40);
const uint64_t dq3 = replicate(c, 0x08);
+ const uint64_t dq2 = replicate(c, 0x04);
uint64_t byte_addr = 0;
for (int region = 0; region < nb_erase_regions; ++region) {
@@ -456,6 +477,95 @@ static void test_geometry(const void *opaque)
}
}
+ /* Test erase suspend/resume during erase timeout. */
+ sector_erase(c, 0);
+ /*
+ * Check that DQ 3 is 0 and DQ6 and DQ2 are toggling in the sector being
+ * erased as well as in a sector not being erased.
+ */
+ byte_addr = c->sector_len[0];
+ status0 = flash_read(c, 0);
+ status1 = flash_read(c, 0);
+ g_assert_cmpint(status0 & dq3, ==, 0);
+ g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6);
+ g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2);
+ status0 = flash_read(c, byte_addr);
+ status1 = flash_read(c, byte_addr);
+ g_assert_cmpint(status0 & dq3, ==, 0);
+ g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6);
+ g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2);
+
+ /*
+ * Check that after suspending, DQ6 does not toggle but DQ2 does toggle in
+ * an erase suspended sector but that neither toggle (we should be
+ * getting data) in a sector not being erased.
+ */
+ erase_suspend(c);
+ status0 = flash_read(c, 0);
+ status1 = flash_read(c, 0);
+ g_assert_cmpint(status0 & dq6, ==, status1 & dq6);
+ g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2);
+ g_assert_cmpint(flash_read(c, byte_addr), ==, flash_read(c, byte_addr));
+
+ /* Check that after resuming, DQ3 is 1 and DQ6 and DQ2 toggle. */
+ erase_resume(c);
+ status0 = flash_read(c, 0);
+ status1 = flash_read(c, 0);
+ g_assert_cmpint(status0 & dq3, ==, dq3);
+ g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6);
+ g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2);
+ status0 = flash_read(c, byte_addr);
+ status1 = flash_read(c, byte_addr);
+ g_assert_cmpint(status0 & dq3, ==, dq3);
+ g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6);
+ g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2);
+ wait_for_completion(c, 0);
+
+ /* Repeat this process but this time suspend after the timeout. */
+ sector_erase(c, 0);
+ qtest_clock_step_next(c->qtest);
+ /*
+ * Check that DQ 3 is 1 and DQ6 and DQ2 are toggling in the sector being
+ * erased as well as in a sector not being erased.
+ */
+ byte_addr = c->sector_len[0];
+ status0 = flash_read(c, 0);
+ status1 = flash_read(c, 0);
+ g_assert_cmpint(status0 & dq3, ==, dq3);
+ g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6);
+ g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2);
+ status0 = flash_read(c, byte_addr);
+ status1 = flash_read(c, byte_addr);
+ g_assert_cmpint(status0 & dq3, ==, dq3);
+ g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6);
+ g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2);
+
+ /*
+ * Check that after suspending, DQ6 does not toggle but DQ2 does toggle in
+ * an erase suspended sector but that neither toggle (we should be
+ * getting data) in a sector not being erased.
+ */
+ erase_suspend(c);
+ status0 = flash_read(c, 0);
+ status1 = flash_read(c, 0);
+ g_assert_cmpint(status0 & dq6, ==, status1 & dq6);
+ g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2);
+ g_assert_cmpint(flash_read(c, byte_addr), ==, flash_read(c, byte_addr));
+
+ /* Check that after resuming, DQ3 is 1 and DQ6 and DQ2 toggle. */
+ erase_resume(c);
+ status0 = flash_read(c, 0);
+ status1 = flash_read(c, 0);
+ g_assert_cmpint(status0 & dq3, ==, dq3);
+ g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6);
+ g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2);
+ status0 = flash_read(c, byte_addr);
+ status1 = flash_read(c, byte_addr);
+ g_assert_cmpint(status0 & dq3, ==, dq3);
+ g_assert_cmpint(status0 & dq6, ==, ~status1 & dq6);
+ g_assert_cmpint(status0 & dq2, ==, ~status1 & dq2);
+ wait_for_completion(c, 0);
+
qtest_quit(qtest);
}