aboutsummaryrefslogtreecommitdiff
path: root/libflash/test/test-flash.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-07-02 15:36:20 +1000
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-07-02 15:36:20 +1000
commit1d880992fd8c8457a2d990ac6622cfd58fb1b261 (patch)
treec4c843b12e96b5612c315db5a23c5da1a900618c /libflash/test/test-flash.c
downloadskiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.zip
skiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.tar.gz
skiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.tar.bz2
Initial commit of Open Source release
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'libflash/test/test-flash.c')
-rw-r--r--libflash/test/test-flash.c418
1 files changed, 418 insertions, 0 deletions
diff --git a/libflash/test/test-flash.c b/libflash/test/test-flash.c
new file mode 100644
index 0000000..5f48797
--- /dev/null
+++ b/libflash/test/test-flash.c
@@ -0,0 +1,418 @@
+/* Copyright 2013-2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <libflash/libflash.h>
+#include <libflash/libflash-priv.h>
+
+#include "../libflash.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 flash_chip *fl;
+ uint32_t total_size, erase_granule;
+ const char *name;
+ uint16_t *test;
+ 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, &fl);
+ if (rc) {
+ ERR("flash_init failed with err %d\n", rc);
+ exit(1);
+ }
+ rc = flash_get_info(fl, &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(fl, 0, test, 0x10000);
+ flash_smart_write(fl, 0x20000, test, 0x10000);
+
+ /* Write "Hello world" straddling the 64k boundary */
+#define HW "Hello World"
+ printf("Writing test string...\n");
+ flash_smart_write(fl, 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");
+ flash_exit(fl);
+
+ return 0;
+}
+