diff options
Diffstat (limited to 'roms/skiboot/libflash/test/test-flash.c')
-rw-r--r-- | roms/skiboot/libflash/test/test-flash.c | 448 |
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; +} |