aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/libflash/test/test-flash.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/libflash/test/test-flash.c')
-rw-r--r--roms/skiboot/libflash/test/test-flash.c448
1 files changed, 448 insertions, 0 deletions
diff --git a/roms/skiboot/libflash/test/test-flash.c b/roms/skiboot/libflash/test/test-flash.c
new file mode 100644
index 000000000..3304195f8
--- /dev/null
+++ b/roms/skiboot/libflash/test/test-flash.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2017 IBM Corp. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <libflash/libflash.h>
+#include <libflash/libflash-priv.h>
+
+#include "../libflash.c"
+#include "../ecc.c"
+
+#define __unused __attribute__((unused))
+
+#define ERR(fmt...) fprintf(stderr, fmt)
+
+/* Flash commands */
+#define CMD_PP 0x02
+#define CMD_READ 0x03
+#define CMD_WRDI 0x04
+#define CMD_RDSR 0x05
+#define CMD_WREN 0x06
+#define CMD_SE 0x20
+#define CMD_RDSCUR 0x2b
+#define CMD_BE32K 0x52
+#define CMD_CE 0x60
+#define CMD_RDID 0x9f
+#define CMD_EN4B 0xb7
+#define CMD_BE 0xd8
+#define CMD_RDDPB 0xe0
+#define CMD_RDSPB 0xe2
+#define CMD_EX4B 0xe9
+
+/* Flash status bits */
+#define STAT_WIP 0x01
+#define STAT_WEN 0x02
+
+static uint8_t *sim_image;
+static uint32_t sim_image_sz = 0x100000;
+static uint32_t sim_index;
+static uint32_t sim_addr;
+static uint32_t sim_er_size;
+static uint8_t sim_sr;
+static bool sim_fl_4b;
+static bool sim_ct_4b;
+
+static enum sim_state {
+ sim_state_idle,
+ sim_state_rdid,
+ sim_state_rdsr,
+ sim_state_read_addr,
+ sim_state_read_data,
+ sim_state_write_addr,
+ sim_state_write_data,
+ sim_state_erase_addr,
+ sim_state_erase_done,
+} sim_state;
+
+/*
+ * Simulated flash & controller
+ */
+static int sim_start_cmd(uint8_t cmd)
+{
+ if (sim_state != sim_state_idle) {
+ ERR("SIM: Command %02x in wrong state %d\n", cmd, sim_state);
+ return -1;
+ }
+
+ sim_index = 0;
+ sim_addr = 0;
+
+ switch(cmd) {
+ case CMD_RDID:
+ sim_state = sim_state_rdid;
+ break;
+ case CMD_RDSR:
+ sim_state = sim_state_rdsr;
+ break;
+ case CMD_EX4B:
+ sim_fl_4b = false;
+ break;
+ case CMD_EN4B:
+ sim_fl_4b = true;
+ break;
+ case CMD_WREN:
+ sim_sr |= STAT_WEN;
+ break;
+ case CMD_READ:
+ sim_state = sim_state_read_addr;
+ if (sim_ct_4b != sim_fl_4b)
+ ERR("SIM: 4b mode mismatch in READ !\n");
+ break;
+ case CMD_PP:
+ sim_state = sim_state_write_addr;
+ if (sim_ct_4b != sim_fl_4b)
+ ERR("SIM: 4b mode mismatch in PP !\n");
+ if (!(sim_sr & STAT_WEN))
+ ERR("SIM: PP without WEN, ignoring... \n");
+ break;
+ case CMD_SE:
+ case CMD_BE32K:
+ case CMD_BE:
+ if (sim_ct_4b != sim_fl_4b)
+ ERR("SIM: 4b mode mismatch in SE/BE !\n");
+ if (!(sim_sr & STAT_WEN))
+ ERR("SIM: SE/BE without WEN, ignoring... \n");
+ sim_state = sim_state_erase_addr;
+ switch(cmd) {
+ case CMD_SE: sim_er_size = 0x1000; break;
+ case CMD_BE32K: sim_er_size = 0x8000; break;
+ case CMD_BE: sim_er_size = 0x10000; break;
+ }
+ break;
+ case CMD_CE:
+ if (!(sim_sr & STAT_WEN)) {
+ ERR("SIM: CE without WEN, ignoring... \n");
+ break;
+ }
+ memset(sim_image, 0xff, sim_image_sz);
+ sim_sr |= STAT_WIP;
+ sim_sr &= ~STAT_WEN;
+ break;
+ default:
+ ERR("SIM: Unsupported command %02x\n", cmd);
+ return -1;
+ }
+ return 0;
+}
+
+static void sim_end_cmd(void)
+{
+ /* For write and sector/block erase, set WIP & clear WEN here */
+ if (sim_state == sim_state_write_data) {
+ sim_sr |= STAT_WIP;
+ sim_sr &= ~STAT_WEN;
+ }
+ sim_state = sim_state_idle;
+}
+
+static bool sim_do_address(const uint8_t **buf, uint32_t *len)
+{
+ uint8_t asize = sim_fl_4b ? 4 : 3;
+ const uint8_t *p = *buf;
+
+ while(*len) {
+ sim_addr = (sim_addr << 8) | *(p++);
+ *buf = p;
+ *len = *len - 1;
+ sim_index++;
+ if (sim_index >= asize)
+ return true;
+ }
+ return false;
+}
+
+static int sim_wbytes(const void *buf, uint32_t len)
+{
+ const uint8_t *b = buf;
+ bool addr_complete;
+
+ again:
+ switch(sim_state) {
+ case sim_state_read_addr:
+ addr_complete = sim_do_address(&b, &len);
+ if (addr_complete) {
+ sim_state = sim_state_read_data;
+ sim_index = 0;
+ if (len)
+ goto again;
+ }
+ break;
+ case sim_state_write_addr:
+ addr_complete = sim_do_address(&b, &len);
+ if (addr_complete) {
+ sim_state = sim_state_write_data;
+ sim_index = 0;
+ if (len)
+ goto again;
+ }
+ break;
+ case sim_state_write_data:
+ if (!(sim_sr & STAT_WEN))
+ break;
+ while(len--) {
+ uint8_t c = *(b++);
+ if (sim_addr >= sim_image_sz) {
+ ERR("SIM: Write past end of flash\n");
+ return -1;
+ }
+ /* Flash write only clears bits */
+ sim_image[sim_addr] &= c;
+ sim_addr = (sim_addr & 0xffffff00) |
+ ((sim_addr + 1) & 0xff);
+ }
+ break;
+ case sim_state_erase_addr:
+ if (!(sim_sr & STAT_WEN))
+ break;
+ addr_complete = sim_do_address(&b, &len);
+ if (addr_complete) {
+ memset(sim_image + sim_addr, 0xff, sim_er_size);
+ sim_sr |= STAT_WIP;
+ sim_sr &= ~STAT_WEN;
+ sim_state = sim_state_erase_done;
+ }
+ break;
+ default:
+ ERR("SIM: Write in wrong state %d\n", sim_state);
+ return -1;
+ }
+ return 0;
+}
+
+static int sim_rbytes(void *buf, uint32_t len)
+{
+ uint8_t *b = buf;
+
+ switch(sim_state) {
+ case sim_state_rdid:
+ while(len--) {
+ switch(sim_index) {
+ case 0:
+ *(b++) = 0x55;
+ break;
+ case 1:
+ *(b++) = 0xaa;
+ break;
+ case 2:
+ *(b++) = 0x55;
+ break;
+ default:
+ ERR("SIM: RDID index %d\n", sim_index);
+ *(b++) = 0;
+ break;
+ }
+ sim_index++;
+ }
+ break;
+ case sim_state_rdsr:
+ while(len--) {
+ *(b++) = sim_sr;
+ if (sim_index > 0)
+ ERR("SIM: RDSR index %d\n", sim_index);
+ sim_index++;
+
+ /* If WIP was 1, clear it, ie, simulate write/erase
+ * completion
+ */
+ sim_sr &= ~STAT_WIP;
+ }
+ break;
+ case sim_state_read_data:
+ while(len--) {
+ if (sim_addr >= sim_image_sz) {
+ ERR("SIM: Read past end of flash\n");
+ return -1;
+ }
+ *(b++) = sim_image[sim_addr++];
+ }
+ break;
+ default:
+ ERR("SIM: Read in wrong state %d\n", sim_state);
+ return -1;
+ }
+ return 0;
+}
+
+static int sim_send_addr(uint32_t addr)
+{
+ const void *ap;
+
+ /* Layout address MSB first in memory */
+ addr = cpu_to_be32(addr);
+
+ /* Send the right amount of bytes */
+ ap = (char *)&addr;
+
+ if (sim_ct_4b)
+ return sim_wbytes(ap, 4);
+ else
+ return sim_wbytes(ap + 1, 3);
+}
+
+static int sim_cmd_rd(struct spi_flash_ctrl *ctrl __unused, uint8_t cmd,
+ bool has_addr, uint32_t addr, void *buffer,
+ uint32_t size)
+{
+ int rc;
+
+ rc = sim_start_cmd(cmd);
+ if (rc)
+ goto bail;
+ if (has_addr) {
+ rc = sim_send_addr(addr);
+ if (rc)
+ goto bail;
+ }
+ if (buffer && size)
+ rc = sim_rbytes(buffer, size);
+ bail:
+ sim_end_cmd();
+ return rc;
+}
+
+static int sim_cmd_wr(struct spi_flash_ctrl *ctrl __unused, uint8_t cmd,
+ bool has_addr, uint32_t addr, const void *buffer,
+ uint32_t size)
+{
+ int rc;
+
+ rc = sim_start_cmd(cmd);
+ if (rc)
+ goto bail;
+ if (has_addr) {
+ rc = sim_send_addr(addr);
+ if (rc)
+ goto bail;
+ }
+ if (buffer && size)
+ rc = sim_wbytes(buffer, size);
+ bail:
+ sim_end_cmd();
+ return rc;
+}
+
+static int sim_set_4b(struct spi_flash_ctrl *ctrl __unused, bool enable)
+{
+ sim_ct_4b = enable;
+
+ return 0;
+}
+
+static int sim_read(struct spi_flash_ctrl *ctrl __unused, uint32_t pos,
+ void *buf, uint32_t len)
+{
+ if (sim_ct_4b != sim_fl_4b)
+ ERR("SIM: 4b mode mismatch in autoread !\n");
+ if ((pos + len) < pos)
+ return -1;
+ if ((pos + len) > sim_image_sz)
+ return -1;
+ memcpy(buf, sim_image + pos, len);
+ return 0;
+};
+
+struct spi_flash_ctrl sim_ctrl = {
+ .cmd_wr = sim_cmd_wr,
+ .cmd_rd = sim_cmd_rd,
+ .set_4b = sim_set_4b,
+ .read = sim_read,
+};
+
+int main(void)
+{
+ struct blocklevel_device *bl;
+ uint64_t total_size;
+ uint32_t erase_granule;
+ const char *name;
+ uint16_t *test;
+ struct ecc64 *ecc_test;
+ uint64_t *test64;
+ int i, rc;
+
+ sim_image = malloc(sim_image_sz);
+ memset(sim_image, 0xff, sim_image_sz);
+ test = malloc(0x10000 * 2);
+
+ rc = flash_init(&sim_ctrl, &bl, NULL);
+ if (rc) {
+ ERR("flash_init failed with err %d\n", rc);
+ exit(1);
+ }
+ rc = flash_get_info(bl, &name, &total_size, &erase_granule);
+ if (rc) {
+ ERR("flash_get_info failed with err %d\n", rc);
+ exit(1);
+ }
+
+ /* Make up a test pattern */
+ for (i=0; i<0x10000;i++)
+ test[i] = cpu_to_be16(i);
+
+ /* Write 64k of stuff at 0 and at 128k */
+ printf("Writing test patterns...\n");
+ flash_smart_write(bl, 0, test, 0x10000);
+ flash_smart_write(bl, 0x20000, test, 0x10000);
+
+ /* Write "Hello world" straddling the 64k boundary */
+#define HW "Hello World"
+ printf("Writing test string...\n");
+ flash_smart_write(bl, 0xfffc, HW, sizeof(HW));
+
+ /* Check result */
+ if (memcmp(sim_image + 0xfffc, HW, sizeof(HW))) {
+ ERR("Test string mismatch !\n");
+ exit(1);
+ }
+ printf("Test string pass\n");
+ if (memcmp(sim_image, test, 0xfffc)) {
+ ERR("Test pattern mismatch !\n");
+ exit(1);
+ }
+ printf("Test pattern pass\n");
+
+ printf("Test ECC interfaces\n");
+ flash_smart_write_corrected(bl, 0, test, 0x10000, 1);
+ ecc_test = (struct ecc64 *)sim_image;
+ test64 = (uint64_t *)test;
+ for (i = 0; i < 0x10000 / sizeof(*ecc_test); i++) {
+ if (test64[i] != ecc_test[i].data) {
+ ERR("flash_smart_write_corrected() pattern missmatch at %d: 0x%016lx vs 0x%016lx\n",
+ i, test64[i], ecc_test[i].data);
+ exit(1);
+ }
+ if (ecc_test[i].ecc != eccgenerate(be64toh(test64[i]))) {
+ ERR("ECCs don't match 0x%02x vs 0x%02x\n", ecc_test[i].ecc, eccgenerate(test64[i]));
+ exit(1);
+ }
+ }
+ printf("Test ECC interface pass\n");
+
+ printf("Test ECC erase\n");
+ if (flash_erase(bl, 0, 0x10000) != 0) {
+ ERR("flash_erase didn't return 0\n");
+ exit(1);
+ }
+
+ for (i = 0; i < 0x10000 / sizeof(*ecc_test); i++) {
+ uint8_t zero = 0;
+ if (ecc_test[i].data != 0xFFFFFFFFFFFFFFFF) {
+ ERR("Data not properly cleared at %d\n", i);
+ exit(1);
+ }
+ rc = flash_write(bl, i * sizeof(*ecc_test) + 8, &zero, 1, 0);
+ if (rc || ecc_test[i].ecc != 0) {
+ ERR("Cleared data not correctly ECCed: 0x%02x (0x%016lx) expecting 0 at %d\n", ecc_test[i].ecc, ecc_test[i].data, i);
+ exit(1);
+ }
+ }
+ printf("Test ECC erase pass\n");
+
+ flash_exit(bl);
+ free(test);
+
+ return 0;
+}