[U-Boot] [PATCH 0/3 v3] sandbox: spi/sf emulation

This should be good to go now I think.
Mike Frysinger (3): sandbox: SPI emulation bus sandbox: new SPI flash driver sandbox: enable new spi/sf layers
arch/sandbox/include/asm/config.h | 8 + arch/sandbox/include/asm/spi.h | 58 ++++++ arch/sandbox/include/asm/state.h | 1 + drivers/mtd/spi/Makefile | 1 + drivers/mtd/spi/sandbox.c | 402 +++++++++++++++++++++++++++++++++++++ drivers/spi/Makefile | 1 + drivers/spi/sandbox_spi.c | 186 +++++++++++++++++ include/configs/sandbox.h | 7 + 8 files changed, 664 insertions(+), 0 deletions(-) create mode 100644 arch/sandbox/include/asm/spi.h create mode 100644 drivers/mtd/spi/sandbox.c create mode 100644 drivers/spi/sandbox_spi.c

This adds a SPI framework for people to hook up simulated SPI clients.
Signed-off-by: Mike Frysinger vapier@gentoo.org --- v3 - rearchitected on top of state/getopt support
arch/sandbox/include/asm/config.h | 8 ++ arch/sandbox/include/asm/spi.h | 58 ++++++++++++ arch/sandbox/include/asm/state.h | 1 + drivers/spi/Makefile | 1 + drivers/spi/sandbox_spi.c | 186 +++++++++++++++++++++++++++++++++++++ 5 files changed, 254 insertions(+), 0 deletions(-) create mode 100644 arch/sandbox/include/asm/spi.h create mode 100644 drivers/spi/sandbox_spi.c
diff --git a/arch/sandbox/include/asm/config.h b/arch/sandbox/include/asm/config.h index 2ef0564..a9590bc 100644 --- a/arch/sandbox/include/asm/config.h +++ b/arch/sandbox/include/asm/config.h @@ -23,4 +23,12 @@
#define CONFIG_SANDBOX_ARCH
+/* Used by drivers/spi/sandbox_spi.c */ +#ifndef CONFIG_SANDBOX_SPI_MAX_BUS +#define CONFIG_SANDBOX_SPI_MAX_BUS 1 +#endif +#ifndef CONFIG_SANDBOX_SPI_MAX_CS +#define CONFIG_SANDBOX_SPI_MAX_CS 10 +#endif + #endif diff --git a/arch/sandbox/include/asm/spi.h b/arch/sandbox/include/asm/spi.h new file mode 100644 index 0000000..f96a926 --- /dev/null +++ b/arch/sandbox/include/asm/spi.h @@ -0,0 +1,58 @@ +/* + * Simulate a SPI port and clients + * + * Copyright (c) 2011-2012 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef __ASM_SPI_H__ +#define __ASM_SPI_H__ + +#include <linux/types.h> + +/* + * The interface between the SPI bus and the SPI client. The bus will + * instantiate a client, and that then call into it via these entry + * points. These should be enough for the client to emulate the SPI + * device just like the real hardware. + */ +struct sb_spi_emu_ops { + /* The bus wants to instantiate a new client, so setup everything */ + int (*setup)(void **priv, const char *spec); + /* The bus is done with us, so break things down */ + void (*free)(void *priv); + /* The CS has been "activated" -- we won't worry about low/high */ + void (*cs_activate)(void *priv); + /* The CS has been "deactivated" -- we won't worry about low/high */ + void (*cs_deactivate)(void *priv); + /* The client is rx-ing bytes from the bus, so it should tx some */ + int (*xfer)(void *priv, const u8 *rx, u8 *tx, uint bytes); +}; + +/* + * There are times when the data lines are allowed to tristate. What + * is actually sensed on the line depends on the hardware. It could + * always be 0xFF/0x00 (if there are pull ups/downs), or things could + * float and so we'd get garbage back. This func encapsulates that + * scenario so we can worry about the details here. + */ +static inline void sb_spi_tristate(u8 *buf, uint len) +{ + /* XXX: make this into a user config option ? */ + memset(buf, 0xff, len); +} + +/* + * Extract the bus/cs from the spi spec and return the start of the spi + * client spec. If the bus/cs are invalid for the current config, then + * it returns NULL. + * + * Example: arg="0:1:foo" will set bus to 0, cs to 1, and return "foo" + */ +const char *sb_spi_parse_spec(const char *arg, unsigned long *bus, + unsigned long *cs); + +#endif diff --git a/arch/sandbox/include/asm/state.h b/arch/sandbox/include/asm/state.h index 2b62b46..ea5fbe0 100644 --- a/arch/sandbox/include/asm/state.h +++ b/arch/sandbox/include/asm/state.h @@ -38,6 +38,7 @@ struct sandbox_state { const char *parse_err; /* Error to report from parsing */ int argc; /* Program arguments */ char **argv; + const void *spi[CONFIG_SANDBOX_SPI_MAX_BUS][CONFIG_SANDBOX_SPI_MAX_CS][2]; };
/** diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c967d87..d80ff8b 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -40,6 +40,7 @@ COBJS-$(CONFIG_MXC_SPI) += mxc_spi.o COBJS-$(CONFIG_MXS_SPI) += mxs_spi.o COBJS-$(CONFIG_OC_TINY_SPI) += oc_tiny_spi.o COBJS-$(CONFIG_OMAP3_SPI) += omap3_spi.o +COBJS-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o COBJS-$(CONFIG_SOFT_SPI) += soft_spi.o COBJS-$(CONFIG_SH_SPI) += sh_spi.o COBJS-$(CONFIG_FSL_ESPI) += fsl_espi.o diff --git a/drivers/spi/sandbox_spi.c b/drivers/spi/sandbox_spi.c new file mode 100644 index 0000000..bb89c7a --- /dev/null +++ b/drivers/spi/sandbox_spi.c @@ -0,0 +1,186 @@ +/* + * Simulate a SPI port + * + * Copyright (c) 2011-2012 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <malloc.h> +#include <spi.h> +#include <os.h> + +#include <asm/spi.h> +#include <asm/state.h> + +#ifndef CONFIG_SPI_IDLE_VAL +# define CONFIG_SPI_IDLE_VAL 0xFF +#endif + +struct sb_spi_slave { + struct spi_slave slave; + const struct sb_spi_emu_ops *ops; + void *priv; +}; + +#define to_sb_spi_slave(s) container_of(s, struct sb_spi_slave, slave) + +const char *sb_spi_parse_spec(const char *arg, unsigned long *bus, unsigned long *cs) +{ + char *endp; + + *bus = simple_strtoul(arg, &endp, 0); + if (*endp != ':' || *bus >= CONFIG_SANDBOX_SPI_MAX_BUS) + return NULL; + + *cs = simple_strtoul(endp + 1, &endp, 0); + if (*endp != ':' || *cs >= CONFIG_SANDBOX_SPI_MAX_CS) + return NULL; + + return endp + 1; +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return bus < CONFIG_SANDBOX_SPI_MAX_BUS && cs < CONFIG_SANDBOX_SPI_MAX_CS; +} + +void spi_cs_activate(struct spi_slave *slave) +{ + struct sb_spi_slave *sss = to_sb_spi_slave(slave); + + debug("sb_spi: activating CS\n"); + if (sss->ops->cs_activate) + sss->ops->cs_activate(sss->priv); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct sb_spi_slave *sss = to_sb_spi_slave(slave); + + debug("sb_spi: deactivating CS\n"); + if (sss->ops->cs_deactivate) + sss->ops->cs_deactivate(sss->priv); +} + +void spi_init(void) +{ +} + +void spi_set_speed(struct spi_slave *slave, uint hz) +{ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct sb_spi_slave *sss; + struct sandbox_state *state = state_get_current(); + const char *spec; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + sss = malloc(sizeof(*sss)); + if (!sss) + return NULL; + + spec = state->spi[bus][cs][1]; + sss->ops = state->spi[bus][cs][0]; + if (!spec || !sss->ops || sss->ops->setup(&sss->priv, spec)) { + free(sss); + printf("sb_spi: unable to locate a slave client\n"); + return NULL; + } + + return &sss->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct sb_spi_slave *sss = to_sb_spi_slave(slave); + + debug("sb_spi: releasing slave\n"); + + if (sss->ops->free) + sss->ops->free(sss->priv); + + free(sss); +} + +static int spi_bus_claim_cnt[CONFIG_SANDBOX_SPI_MAX_BUS]; + +int spi_claim_bus(struct spi_slave *slave) +{ + if (spi_bus_claim_cnt[slave->bus]++) + printf("sb_spi: error: bus already claimed!\n"); + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + if (--spi_bus_claim_cnt[slave->bus]) + printf("sb_spi: error: bus freed too often!\n"); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct sb_spi_slave *sss = to_sb_spi_slave(slave); + uint bytes = bitlen / 8, i; + int ret = 0; + u8 *tx = (void *)dout, *rx = din; + + if (bitlen == 0) + goto done; + + /* we can only do 8 bit transfers */ + if (bitlen % 8) { + printf("sb_spi: xfer: invalid bitlen size %u; needs to be 8bit\n", + bitlen); + flags |= SPI_XFER_END; + goto done; + } + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + /* make sure rx/tx buffers are full so clients can assume */ + if (!tx) { + debug("sb_spi: xfer: auto-allocating tx scratch buffer\n"); + tx = malloc(bytes); + if (tx) + memset(tx, CONFIG_SPI_IDLE_VAL, bytes); + } + if (!rx) { + debug("sb_spi: xfer: auto-allocating rx scratch buffer\n"); + rx = malloc(bytes); + } + if (tx && rx) { + debug("sb_spi: xfer: bytes = %u\n tx:", bytes); + for (i = 0; i < bytes; ++i) + debug(" %u:%02x", i, tx[i]); + debug("\n"); + + ret = sss->ops->xfer(sss->priv, tx, rx, bytes); + + debug("sb_spi: xfer: got back %i (that's %s)\n rx:", + ret, ret ? "bad" : "good"); + for (i = 0; i < bytes; ++i) + debug(" %u:%02x", i, rx[i]); + debug("\n"); + } + if (tx != dout) + free(tx); + if (rx != din) + free(rx); + + done: + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return ret; +}

Hi Mike,
On Mon, Mar 12, 2012 at 8:22 AM, Mike Frysinger vapier@gentoo.org wrote:
This adds a SPI framework for people to hook up simulated SPI clients.
Signed-off-by: Mike Frysinger vapier@gentoo.org
v3 - rearchitected on top of state/getopt support
arch/sandbox/include/asm/config.h | 8 ++ arch/sandbox/include/asm/spi.h | 58 ++++++++++++ arch/sandbox/include/asm/state.h | 1 + drivers/spi/Makefile | 1 + drivers/spi/sandbox_spi.c | 186 +++++++++++++++++++++++++++++++++++++ 5 files changed, 254 insertions(+), 0 deletions(-) create mode 100644 arch/sandbox/include/asm/spi.h create mode 100644 drivers/spi/sandbox_spi.c
diff --git a/arch/sandbox/include/asm/config.h b/arch/sandbox/include/asm/config.h index 2ef0564..a9590bc 100644 --- a/arch/sandbox/include/asm/config.h +++ b/arch/sandbox/include/asm/config.h @@ -23,4 +23,12 @@
#define CONFIG_SANDBOX_ARCH
+/* Used by drivers/spi/sandbox_spi.c */ +#ifndef CONFIG_SANDBOX_SPI_MAX_BUS +#define CONFIG_SANDBOX_SPI_MAX_BUS 1 +#endif +#ifndef CONFIG_SANDBOX_SPI_MAX_CS +#define CONFIG_SANDBOX_SPI_MAX_CS 10 +#endif
Why not put this at the top of that driver? Also shouldn't these be added to the README?
#endif diff --git a/arch/sandbox/include/asm/spi.h b/arch/sandbox/include/asm/spi.h new file mode 100644 index 0000000..f96a926 --- /dev/null +++ b/arch/sandbox/include/asm/spi.h @@ -0,0 +1,58 @@ +/*
- Simulate a SPI port and clients
It would be nice if you could add a few more details about what is simulated and how to use it.
- Copyright (c) 2011-2012 The Chromium OS Authors.
- See file CREDITS for list of people who contributed to this
- project.
- Licensed under the GPL-2 or later.
- */
+#ifndef __ASM_SPI_H__ +#define __ASM_SPI_H__
+#include <linux/types.h>
+/*
- The interface between the SPI bus and the SPI client. The bus will
- instantiate a client, and that then call into it via these entry
- points. These should be enough for the client to emulate the SPI
- device just like the real hardware.
- */
+struct sb_spi_emu_ops {
I would really like to have sandbox_ written out instead of sb_. I thought you were ok with that for exported functions / structures at least?
- /* The bus wants to instantiate a new client, so setup everything */
- int (*setup)(void **priv, const char *spec);
- /* The bus is done with us, so break things down */
- void (*free)(void *priv);
- /* The CS has been "activated" -- we won't worry about low/high */
- void (*cs_activate)(void *priv);
- /* The CS has been "deactivated" -- we won't worry about low/high */
- void (*cs_deactivate)(void *priv);
- /* The client is rx-ing bytes from the bus, so it should tx some */
- int (*xfer)(void *priv, const u8 *rx, u8 *tx, uint bytes);
Can we have full docs on each of these functions? They look similar to spi.h but not the same?
+};
+/*
- There are times when the data lines are allowed to tristate. What
- is actually sensed on the line depends on the hardware. It could
- always be 0xFF/0x00 (if there are pull ups/downs), or things could
- float and so we'd get garbage back. This func encapsulates that
- scenario so we can worry about the details here.
- */
+static inline void sb_spi_tristate(u8 *buf, uint len) +{
- /* XXX: make this into a user config option ? */
- memset(buf, 0xff, len);
+}
+/*
- Extract the bus/cs from the spi spec and return the start of the spi
- client spec. If the bus/cs are invalid for the current config, then
- it returns NULL.
- Example: arg="0:1:foo" will set bus to 0, cs to 1, and return "foo"
- */
+const char *sb_spi_parse_spec(const char *arg, unsigned long *bus,
- unsigned long *cs);
+#endif diff --git a/arch/sandbox/include/asm/state.h b/arch/sandbox/include/asm/state.h index 2b62b46..ea5fbe0 100644 --- a/arch/sandbox/include/asm/state.h +++ b/arch/sandbox/include/asm/state.h @@ -38,6 +38,7 @@ struct sandbox_state { const char *parse_err; /* Error to report from parsing */ int argc; /* Program arguments */ char **argv;
- const void *spi[CONFIG_SANDBOX_SPI_MAX_BUS][CONFIG_SANDBOX_SPI_MAX_CS][2];
OK now I see why you need it in config.h.
};
/** diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c967d87..d80ff8b 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -40,6 +40,7 @@ COBJS-$(CONFIG_MXC_SPI) += mxc_spi.o COBJS-$(CONFIG_MXS_SPI) += mxs_spi.o COBJS-$(CONFIG_OC_TINY_SPI) += oc_tiny_spi.o COBJS-$(CONFIG_OMAP3_SPI) += omap3_spi.o +COBJS-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o COBJS-$(CONFIG_SOFT_SPI) += soft_spi.o COBJS-$(CONFIG_SH_SPI) += sh_spi.o COBJS-$(CONFIG_FSL_ESPI) += fsl_espi.o diff --git a/drivers/spi/sandbox_spi.c b/drivers/spi/sandbox_spi.c new file mode 100644 index 0000000..bb89c7a --- /dev/null +++ b/drivers/spi/sandbox_spi.c @@ -0,0 +1,186 @@ +/*
- Simulate a SPI port
- Copyright (c) 2011-2012 The Chromium OS Authors.
- See file CREDITS for list of people who contributed to this
- project.
- Licensed under the GPL-2 or later.
- */
+#include <common.h> +#include <malloc.h> +#include <spi.h> +#include <os.h>
+#include <asm/spi.h> +#include <asm/state.h>
+#ifndef CONFIG_SPI_IDLE_VAL +# define CONFIG_SPI_IDLE_VAL 0xFF +#endif
Add to README?
+struct sb_spi_slave {
- struct spi_slave slave;
- const struct sb_spi_emu_ops *ops;
- void *priv;
+};
+#define to_sb_spi_slave(s) container_of(s, struct sb_spi_slave, slave)
+const char *sb_spi_parse_spec(const char *arg, unsigned long *bus, unsigned long *cs) +{
- char *endp;
- *bus = simple_strtoul(arg, &endp, 0);
- if (*endp != ':' || *bus >= CONFIG_SANDBOX_SPI_MAX_BUS)
- return NULL;
- *cs = simple_strtoul(endp + 1, &endp, 0);
- if (*endp != ':' || *cs >= CONFIG_SANDBOX_SPI_MAX_CS)
- return NULL;
- return endp + 1;
+}
+int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{
- return bus < CONFIG_SANDBOX_SPI_MAX_BUS && cs < CONFIG_SANDBOX_SPI_MAX_CS;
+}
+void spi_cs_activate(struct spi_slave *slave) +{
- struct sb_spi_slave *sss = to_sb_spi_slave(slave);
- debug("sb_spi: activating CS\n");
- if (sss->ops->cs_activate)
- sss->ops->cs_activate(sss->priv);
+}
+void spi_cs_deactivate(struct spi_slave *slave) +{
- struct sb_spi_slave *sss = to_sb_spi_slave(slave);
- debug("sb_spi: deactivating CS\n");
- if (sss->ops->cs_deactivate)
- sss->ops->cs_deactivate(sss->priv);
+}
+void spi_init(void) +{ +}
+void spi_set_speed(struct spi_slave *slave, uint hz) +{ +}
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
- unsigned int max_hz, unsigned int mode)
+{
- struct sb_spi_slave *sss;
- struct sandbox_state *state = state_get_current();
- const char *spec;
- if (!spi_cs_is_valid(bus, cs))
- return NULL;
- sss = malloc(sizeof(*sss));
- if (!sss)
- return NULL;
Do we want debug() for those two returns?
- spec = state->spi[bus][cs][1];
- sss->ops = state->spi[bus][cs][0];
- if (!spec || !sss->ops || sss->ops->setup(&sss->priv, spec)) {
- free(sss);
- printf("sb_spi: unable to locate a slave client\n");
- return NULL;
- }
- return &sss->slave;
+}
+void spi_free_slave(struct spi_slave *slave) +{
- struct sb_spi_slave *sss = to_sb_spi_slave(slave);
- debug("sb_spi: releasing slave\n");
- if (sss->ops->free)
- sss->ops->free(sss->priv);
- free(sss);
+}
+static int spi_bus_claim_cnt[CONFIG_SANDBOX_SPI_MAX_BUS];
+int spi_claim_bus(struct spi_slave *slave) +{
- if (spi_bus_claim_cnt[slave->bus]++)
- printf("sb_spi: error: bus already claimed!\n");
- return 0;
+}
+void spi_release_bus(struct spi_slave *slave) +{
- if (--spi_bus_claim_cnt[slave->bus])
- printf("sb_spi: error: bus freed too often!\n");
Can we assert() here also?
+}
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
- void *din, unsigned long flags)
+{
- struct sb_spi_slave *sss = to_sb_spi_slave(slave);
- uint bytes = bitlen / 8, i;
- int ret = 0;
- u8 *tx = (void *)dout, *rx = din;
- if (bitlen == 0)
- goto done;
- /* we can only do 8 bit transfers */
- if (bitlen % 8) {
- printf("sb_spi: xfer: invalid bitlen size %u; needs to be 8bit\n",
- bitlen);
- flags |= SPI_XFER_END;
- goto done;
- }
- if (flags & SPI_XFER_BEGIN)
- spi_cs_activate(slave);
- /* make sure rx/tx buffers are full so clients can assume */
- if (!tx) {
- debug("sb_spi: xfer: auto-allocating tx scratch buffer\n");
- tx = malloc(bytes);
- if (tx)
- memset(tx, CONFIG_SPI_IDLE_VAL, bytes);
- }
- if (!rx) {
- debug("sb_spi: xfer: auto-allocating rx scratch buffer\n");
- rx = malloc(bytes);
assert()?
I'm not sure what is supposed to happen if rx / tx allow fails. It seems that things are then broken, yet we continue regardless.
- }
- if (tx && rx) {
- debug("sb_spi: xfer: bytes = %u\n tx:", bytes);
- for (i = 0; i < bytes; ++i)
- debug(" %u:%02x", i, tx[i]);
- debug("\n");
- ret = sss->ops->xfer(sss->priv, tx, rx, bytes);
- debug("sb_spi: xfer: got back %i (that's %s)\n rx:",
- ret, ret ? "bad" : "good");
- for (i = 0; i < bytes; ++i)
- debug(" %u:%02x", i, rx[i]);
- debug("\n");
- }
- if (tx != dout)
- free(tx);
- if (rx != din)
- free(rx);
- done:
- if (flags & SPI_XFER_END)
- spi_cs_deactivate(slave);
- return ret;
+}
1.7.8.5
Regards, Simon

This adds a SPI flash driver which simulates SPI flash clients. Currently supports the bare min that U-Boot requires: you can probe, read, erase, and write. Should be easy to extend to make it behave more exactly like a real SPI flash, but this is good enough to merge now.
Signed-off-by: Mike Frysinger vapier@gentoo.org --- v3 - rearchitected on top of state/getopt support
drivers/mtd/spi/Makefile | 1 + drivers/mtd/spi/sandbox.c | 402 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 403 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/spi/sandbox.c
diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 90f8392..fb37807 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -33,6 +33,7 @@ COBJS-$(CONFIG_SPI_FLASH) += spi_flash.o COBJS-$(CONFIG_SPI_FLASH_ATMEL) += atmel.o COBJS-$(CONFIG_SPI_FLASH_EON) += eon.o COBJS-$(CONFIG_SPI_FLASH_MACRONIX) += macronix.o +COBJS-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o COBJS-$(CONFIG_SPI_FLASH_SPANSION) += spansion.o COBJS-$(CONFIG_SPI_FLASH_SST) += sst.o COBJS-$(CONFIG_SPI_FLASH_STMICRO) += stmicro.o diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c new file mode 100644 index 0000000..1cbe454 --- /dev/null +++ b/drivers/mtd/spi/sandbox.c @@ -0,0 +1,402 @@ +/* + * Simulate a SPI flash + * + * Copyright (c) 2011-2012 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <malloc.h> +#include <spi.h> +#include <os.h> + +#include <spi_flash.h> +#include "spi_flash_internal.h" + +#include <asm/getopt.h> +#include <asm/spi.h> +#include <asm/state.h> + +/* + * The different states that our SPI flash transitions between. + * We need to keep track of this across multiple xfer calls since + * the SPI bus could possibly call down into us multiple times. + */ +typedef enum { + SF_CMD, /* default state -- we're awaiting a command */ + SF_ID, /* read the flash's (jedec) ID code */ + SF_ADDR, /* processing the offset in the flash to read/etc... */ + SF_READ, /* reading data from the flash */ + SF_WRITE, /* writing data to the flash, i.e. page programming */ + SF_ERASE, /* erase the flash */ + SF_READ_STATUS, /* read the flash's status register */ +} sb_sf_state; + +static const char *sb_sf_state_name(sb_sf_state state) +{ + static const char * const states[] = { + "CMD", "ID", "ADDR", "READ", "WRITE", "ERASE", "READ_STATUS", + }; + return states[state]; +} + +/* Bits for the status register */ +#define STAT_WIP (1 << 0) +#define STAT_WEL (1 << 1) + +/* Assume all SPI flashes have 3 byte addresses since they do atm */ +#define SF_ADDR_LEN 3 + +struct sb_spi_flash_erase_commands { + u8 cmd; + u32 size; +}; +#define IDCODE_LEN 5 +#define MAX_ERASE_CMDS 2 +struct sb_spi_flash_data { + const char *name; + u8 idcode[IDCODE_LEN]; + u32 size; + const struct sb_spi_flash_erase_commands erase_cmds[MAX_ERASE_CMDS]; +}; + +/* Structure describing all the flashes we know how to emulate */ +static const struct sb_spi_flash_data sb_sf_flashes[] = { + { + "M25P16", { 0x20, 0x20, 0x15 }, (2 * 1024 * 1024), + { /* erase commands */ + { 0xd8, (64 * 1024), }, /* sector */ + { 0xc7, (2 * 1024 * 1024), }, /* bulk */ + }, + }, +}; + +/* Used to quickly bulk erase backing store */ +static u8 sb_sf_0xff[0x10000]; + +/* Internal state data for each SPI flash */ +struct sb_spi_flash { + /* + * As we receive data over the SPI bus, our flash transitions + * between states. For example, we start off in the SF_CMD + * state where the first byte tells us what operation to perform + * (such as read or write the flash). But the operation itself + * can go through a few states such as first reading in the + * offset in the flash to perform the requested operation. + * Thus "state" stores the exact state that our machine is in + * while "cmd" stores the overall command we're processing. + */ + sb_sf_state state; + uint cmd; + const void *cmd_data; + /* Current position in the flash; used when reading/writing/etc... */ + uint off; + /* How many address bytes we've consumed */ + uint addr_bytes, pad_addr_bytes; + /* The current flash status (see STAT_XXX defines above) */ + u8 status; + /* Data describing the flash we're emulating */ + const struct sb_spi_flash_data *data; + /* The file on disk to serv up data from */ + int fd; +}; + +static int sb_sf_setup(void **priv, const char *spec) +{ + /* spec = idcode:file */ + struct sb_spi_flash *sbsf; + const char *file; + size_t i, len, idname_len; + const struct sb_spi_flash_data *data; + + file = strchr(spec, ':'); + if (!file) + goto error; + idname_len = file - spec; + ++file; + + for (i = 0; i < ARRAY_SIZE(sb_sf_flashes); ++i) { + data = &sb_sf_flashes[i]; + len = strlen(data->name); + if (idname_len != len) + continue; + if (!memcmp(spec, data->name, len)) + break; + } + if (i == ARRAY_SIZE(sb_sf_flashes)) { + printf("sb_sf: unknown flash '%*s'\n", + (int)idname_len, file); + goto error; + } + + if (sb_sf_0xff[0] == 0x00) + memset(sb_sf_0xff, 0xff, sizeof(sb_sf_0xff)); + + sbsf = malloc(sizeof(*sbsf)); + if (!sbsf) + goto error; + + sbsf->fd = os_open(file, 02); + if (sbsf->fd == -1) { + free(sbsf); + goto error; + } + + sbsf->data = data; + + *priv = sbsf; + return 0; + + error: + printf("sb_sf: unable to parse client spec\n"); + return 1; +} + +static void sb_sf_free(void *priv) +{ + struct sb_spi_flash *sbsf = priv; + + os_close(sbsf->fd); + free(sbsf); +} + +static void sb_sf_cs_activate(void *priv) +{ + struct sb_spi_flash *sbsf = priv; + + debug("sb_sf: CS activated; state is fresh!\n"); + + /* CS is asserted, so reset state */ + sbsf->off = 0; + sbsf->addr_bytes = 0; + sbsf->pad_addr_bytes = 0; + sbsf->state = sbsf->cmd = SF_CMD; +} + +static void sb_sf_cs_deactivate(void *priv) +{ + debug("sb_sf: CS deactivated; cmd done processing!\n"); +} + +/* Figure out what command this stream is telling us to do */ +static int sb_sf_process_cmd(struct sb_spi_flash *sbsf, const u8 *rx, u8 *tx) +{ + sb_sf_state oldstate = sbsf->state; + + /* We need to output a byte for the cmd byte we just ate */ + sb_spi_tristate(tx, 1); + + sbsf->cmd = rx[0]; + switch (sbsf->cmd) { + case CMD_READ_ID: + sbsf->state = sbsf->cmd = SF_ID; + break; + case CMD_READ_ARRAY_FAST: + sbsf->pad_addr_bytes = 1; + case CMD_READ_ARRAY_SLOW: + case CMD_PAGE_PROGRAM: + state_addr: + sbsf->state = SF_ADDR; + break; + case CMD_WRITE_DISABLE: + debug(" write disabled\n"); + sbsf->status &= ~STAT_WEL; + break; + case CMD_READ_STATUS: + sbsf->state = SF_READ_STATUS; + break; + case CMD_WRITE_ENABLE: + debug(" write enabled\n"); + sbsf->status |= STAT_WEL; + break; + default: { + size_t i; + + /* handle erase commands first */ + for (i = 0; i < MAX_ERASE_CMDS; ++i) { + const struct sb_spi_flash_erase_commands *erase_cmd = + &sbsf->data->erase_cmds[i]; + + if (erase_cmd->cmd == 0x00) + continue; + if (sbsf->cmd != erase_cmd->cmd) + continue; + + sbsf->cmd_data = erase_cmd; + goto state_addr; + } + + debug(" cmd unknown: %#x\n", sbsf->cmd); + return 1; + } + } + + if (oldstate != sbsf->state) + debug(" cmd: transition to %s state\n", + sb_sf_state_name(sbsf->state)); + + return 0; +} + +static int sb_sf_xfer(void *priv, const u8 *rx, u8 *tx, + uint bytes) +{ + struct sb_spi_flash *sbsf = priv; + uint cnt, pos = 0; + + debug("sb_sf: state:%x(%s) bytes:%u\n", sbsf->state, + sb_sf_state_name(sbsf->state), bytes); + + if (sbsf->state == SF_CMD) { + /* Figure out the initial state */ + if (sb_sf_process_cmd(sbsf, rx, tx)) + return 1; + ++pos; + } + + /* Process the remaining data */ + while (pos < bytes) { + switch (sbsf->state) { + case SF_ID: { + u8 id; + + debug(" id: off:%u tx:", sbsf->off); + if (sbsf->off < IDCODE_LEN) + id = sbsf->data->idcode[sbsf->off]; + else + id = 0; + debug("%02x\n", id); + tx[pos++] = id; + ++sbsf->off; + break; + } + case SF_ADDR: + debug(" addr: bytes:%u rx:%02x ", + sbsf->addr_bytes, rx[pos]); + + if (sbsf->addr_bytes++ < SF_ADDR_LEN) + sbsf->off = (sbsf->off << 8) | rx[pos]; + debug("addr:%06x\n", sbsf->off); + + sb_spi_tristate(&tx[pos++], 1); + + /* See if we're done processing */ + if (sbsf->addr_bytes < SF_ADDR_LEN + sbsf->pad_addr_bytes) + break; + + /* Next state! */ + os_lseek(sbsf->fd, sbsf->off, OS_SEEK_SET); + switch (sbsf->cmd) { + case CMD_READ_ARRAY_FAST: + case CMD_READ_ARRAY_SLOW: + sbsf->state = SF_READ; + break; + case CMD_PAGE_PROGRAM: + sbsf->state = SF_WRITE; + break; + default: + /* assume erase state ... */ + sbsf->state = SF_ERASE; + goto case_SF_ERASE; + } + debug(" cmd: transition to %s state\n", + sb_sf_state_name(sbsf->state)); + break; + case SF_READ: + /* + * XXX: need to handle exotic behavior: + * - reading past end of device + */ + + cnt = bytes - pos; + debug(" tx: read(%u)\n", cnt); + pos += os_read(sbsf->fd, tx + pos, cnt); + break; + case SF_READ_STATUS: + debug(" read status: %#x\n", sbsf->status); + cnt = bytes - pos; + memset(tx + pos, sbsf->status, cnt); + pos += cnt; + break; + case SF_WRITE: + /* + * XXX: need to handle exotic behavior: + * - unaligned addresses + * - more than a page (256) worth of data + * - reading past end of device + */ + + if (!(sbsf->status & STAT_WEL)) { + puts("sb_sf: write enable not set before erase\n"); + goto done; + } + + cnt = bytes - pos; + debug(" rx: write(%u)\n", cnt); + sb_spi_tristate(&tx[pos], cnt); + pos += os_write(sbsf->fd, rx + pos, cnt); + sbsf->status &= ~STAT_WEL; + break; + case SF_ERASE: + case_SF_ERASE: { + const struct sb_spi_flash_erase_commands *erase_cmd = + sbsf->cmd_data; + + if (!(sbsf->status & STAT_WEL)) { + puts("sb_sf: write enable not set before erase\n"); + goto done; + } + + /* verify address is aligned */ + if (sbsf->off & ~(erase_cmd->size - 1)) { + debug(" sector erase: cmd:%#x needs align:%#x, but we got %#x\n", + erase_cmd->cmd, erase_cmd->size, sbsf->off); + sbsf->status &= ~STAT_WEL; + goto done; + } + + debug(" sector erase addr: %u\n", sbsf->off); + + cnt = bytes - pos; + sb_spi_tristate(&tx[pos], cnt); + pos += cnt; + + /* XXX: latch WIP in status, and delay before clearing it ? */ + os_write(sbsf->fd, sb_sf_0xff, erase_cmd->size); + sbsf->status &= ~STAT_WEL; + goto done; + } + default: + debug(" ??? no idea what to do ???\n"); + goto done; + } + } + + done: + return pos == bytes ? 0 : 1; +} + +static const struct sb_spi_emu_ops sb_sf_ops = { + .setup = sb_sf_setup, + .free = sb_sf_free, + .cs_activate = sb_sf_cs_activate, + .cs_deactivate = sb_sf_cs_deactivate, + .xfer = sb_sf_xfer, +}; + +static int sb_cmdline_cb_spi_sf(struct sandbox_state *state, const char *arg) +{ + unsigned long bus, cs; + const char *spec = sb_spi_parse_spec(arg, &bus, &cs); + + if (!spec) + return 1; + + state->spi[bus][cs][0] = &sb_sf_ops; + state->spi[bus][cs][1] = spec; + return 0; +} +SB_CMDLINE_OPT(spi_sf, 1, "connect a SPI flash: <bus>:<cs>:<id>:<file>");

Hi Mike,
On Mon, Mar 12, 2012 at 8:22 AM, Mike Frysinger vapier@gentoo.org wrote:
This adds a SPI flash driver which simulates SPI flash clients. Currently supports the bare min that U-Boot requires: you can probe, read, erase, and write. Should be easy to extend to make it behave more exactly like a real SPI flash, but this is good enough to merge now.
Signed-off-by: Mike Frysinger vapier@gentoo.org
Looks really nice. I have a few nits below.
v3 - rearchitected on top of state/getopt support
drivers/mtd/spi/Makefile | 1 + drivers/mtd/spi/sandbox.c | 402 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 403 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/spi/sandbox.c
diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 90f8392..fb37807 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -33,6 +33,7 @@ COBJS-$(CONFIG_SPI_FLASH) += spi_flash.o COBJS-$(CONFIG_SPI_FLASH_ATMEL) += atmel.o COBJS-$(CONFIG_SPI_FLASH_EON) += eon.o COBJS-$(CONFIG_SPI_FLASH_MACRONIX) += macronix.o +COBJS-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o COBJS-$(CONFIG_SPI_FLASH_SPANSION) += spansion.o COBJS-$(CONFIG_SPI_FLASH_SST) += sst.o COBJS-$(CONFIG_SPI_FLASH_STMICRO) += stmicro.o diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c new file mode 100644 index 0000000..1cbe454 --- /dev/null +++ b/drivers/mtd/spi/sandbox.c @@ -0,0 +1,402 @@ +/*
- Simulate a SPI flash
- Copyright (c) 2011-2012 The Chromium OS Authors.
- See file CREDITS for list of people who contributed to this
- project.
- Licensed under the GPL-2 or later.
- */
+#include <common.h> +#include <malloc.h> +#include <spi.h> +#include <os.h>
+#include <spi_flash.h> +#include "spi_flash_internal.h"
+#include <asm/getopt.h> +#include <asm/spi.h> +#include <asm/state.h>
+/*
- The different states that our SPI flash transitions between.
- We need to keep track of this across multiple xfer calls since
- the SPI bus could possibly call down into us multiple times.
- */
+typedef enum {
- SF_CMD, /* default state -- we're awaiting a command */
- SF_ID, /* read the flash's (jedec) ID code */
- SF_ADDR, /* processing the offset in the flash to read/etc... */
- SF_READ, /* reading data from the flash */
- SF_WRITE, /* writing data to the flash, i.e. page programming */
- SF_ERASE, /* erase the flash */
- SF_READ_STATUS, /* read the flash's status register */
+} sb_sf_state;
+static const char *sb_sf_state_name(sb_sf_state state) +{
- static const char * const states[] = {
- "CMD", "ID", "ADDR", "READ", "WRITE", "ERASE", "READ_STATUS",
- };
- return states[state];
+}
+/* Bits for the status register */ +#define STAT_WIP (1 << 0) +#define STAT_WEL (1 << 1)
+/* Assume all SPI flashes have 3 byte addresses since they do atm */ +#define SF_ADDR_LEN 3
+struct sb_spi_flash_erase_commands {
- u8 cmd;
- u32 size;
+}; +#define IDCODE_LEN 5 +#define MAX_ERASE_CMDS 2 +struct sb_spi_flash_data {
- const char *name;
- u8 idcode[IDCODE_LEN];
- u32 size;
- const struct sb_spi_flash_erase_commands erase_cmds[MAX_ERASE_CMDS];
+};
+/* Structure describing all the flashes we know how to emulate */ +static const struct sb_spi_flash_data sb_sf_flashes[] = {
- {
- "M25P16", { 0x20, 0x20, 0x15 }, (2 * 1024 * 1024),
- { /* erase commands */
- { 0xd8, (64 * 1024), }, /* sector */
- { 0xc7, (2 * 1024 * 1024), }, /* bulk */
- },
- },
+};
+/* Used to quickly bulk erase backing store */ +static u8 sb_sf_0xff[0x10000];
Ick, Does it really need to be so large?
+/* Internal state data for each SPI flash */ +struct sb_spi_flash {
- /*
- * As we receive data over the SPI bus, our flash transitions
- * between states. For example, we start off in the SF_CMD
- * state where the first byte tells us what operation to perform
- * (such as read or write the flash). But the operation itself
- * can go through a few states such as first reading in the
- * offset in the flash to perform the requested operation.
- * Thus "state" stores the exact state that our machine is in
- * while "cmd" stores the overall command we're processing.
- */
- sb_sf_state state;
- uint cmd;
- const void *cmd_data;
- /* Current position in the flash; used when reading/writing/etc... */
- uint off;
- /* How many address bytes we've consumed */
- uint addr_bytes, pad_addr_bytes;
- /* The current flash status (see STAT_XXX defines above) */
- u8 status;
- /* Data describing the flash we're emulating */
- const struct sb_spi_flash_data *data;
- /* The file on disk to serv up data from */
- int fd;
+};
+static int sb_sf_setup(void **priv, const char *spec) +{
- /* spec = idcode:file */
- struct sb_spi_flash *sbsf;
- const char *file;
- size_t i, len, idname_len;
- const struct sb_spi_flash_data *data;
- file = strchr(spec, ':');
- if (!file)
- goto error;
- idname_len = file - spec;
- ++file;
- for (i = 0; i < ARRAY_SIZE(sb_sf_flashes); ++i) {
- data = &sb_sf_flashes[i];
- len = strlen(data->name);
- if (idname_len != len)
- continue;
- if (!memcmp(spec, data->name, len))
- break;
- }
- if (i == ARRAY_SIZE(sb_sf_flashes)) {
- printf("sb_sf: unknown flash '%*s'\n",
- (int)idname_len, file);
- goto error;
- }
- if (sb_sf_0xff[0] == 0x00)
- memset(sb_sf_0xff, 0xff, sizeof(sb_sf_0xff));
- sbsf = malloc(sizeof(*sbsf));
- if (!sbsf)
- goto error;
- sbsf->fd = os_open(file, 02);
- if (sbsf->fd == -1) {
- free(sbsf);
- goto error;
Prints incorrect error if the file couldn't be found/open. I wonder if we should have os_perror() ?
- }
- sbsf->data = data;
- *priv = sbsf;
- return 0;
- error:
- printf("sb_sf: unable to parse client spec\n");
- return 1;
+}
+static void sb_sf_free(void *priv) +{
- struct sb_spi_flash *sbsf = priv;
- os_close(sbsf->fd);
- free(sbsf);
+}
+static void sb_sf_cs_activate(void *priv) +{
- struct sb_spi_flash *sbsf = priv;
- debug("sb_sf: CS activated; state is fresh!\n");
- /* CS is asserted, so reset state */
- sbsf->off = 0;
- sbsf->addr_bytes = 0;
- sbsf->pad_addr_bytes = 0;
- sbsf->state = sbsf->cmd = SF_CMD;
+}
+static void sb_sf_cs_deactivate(void *priv) +{
- debug("sb_sf: CS deactivated; cmd done processing!\n");
+}
+/* Figure out what command this stream is telling us to do */ +static int sb_sf_process_cmd(struct sb_spi_flash *sbsf, const u8 *rx, u8 *tx) +{
- sb_sf_state oldstate = sbsf->state;
- /* We need to output a byte for the cmd byte we just ate */
- sb_spi_tristate(tx, 1);
- sbsf->cmd = rx[0];
- switch (sbsf->cmd) {
- case CMD_READ_ID:
- sbsf->state = sbsf->cmd = SF_ID;
- break;
- case CMD_READ_ARRAY_FAST:
- sbsf->pad_addr_bytes = 1;
- case CMD_READ_ARRAY_SLOW:
- case CMD_PAGE_PROGRAM:
- state_addr:
- sbsf->state = SF_ADDR;
- break;
- case CMD_WRITE_DISABLE:
- debug(" write disabled\n");
- sbsf->status &= ~STAT_WEL;
- break;
- case CMD_READ_STATUS:
- sbsf->state = SF_READ_STATUS;
- break;
- case CMD_WRITE_ENABLE:
- debug(" write enabled\n");
- sbsf->status |= STAT_WEL;
- break;
- default: {
- size_t i;
- /* handle erase commands first */
- for (i = 0; i < MAX_ERASE_CMDS; ++i) {
- const struct sb_spi_flash_erase_commands *erase_cmd =
- &sbsf->data->erase_cmds[i];
- if (erase_cmd->cmd == 0x00)
- continue;
- if (sbsf->cmd != erase_cmd->cmd)
- continue;
- sbsf->cmd_data = erase_cmd;
- goto state_addr;
- }
- debug(" cmd unknown: %#x\n", sbsf->cmd);
- return 1;
- }
- }
- if (oldstate != sbsf->state)
- debug(" cmd: transition to %s state\n",
- sb_sf_state_name(sbsf->state));
- return 0;
+}
+static int sb_sf_xfer(void *priv, const u8 *rx, u8 *tx,
- uint bytes)
+{
- struct sb_spi_flash *sbsf = priv;
- uint cnt, pos = 0;
- debug("sb_sf: state:%x(%s) bytes:%u\n", sbsf->state,
- sb_sf_state_name(sbsf->state), bytes);
- if (sbsf->state == SF_CMD) {
- /* Figure out the initial state */
- if (sb_sf_process_cmd(sbsf, rx, tx))
- return 1;
- ++pos;
- }
- /* Process the remaining data */
- while (pos < bytes) {
- switch (sbsf->state) {
- case SF_ID: {
- u8 id;
- debug(" id: off:%u tx:", sbsf->off);
- if (sbsf->off < IDCODE_LEN)
- id = sbsf->data->idcode[sbsf->off];
- else
- id = 0;
- debug("%02x\n", id);
- tx[pos++] = id;
- ++sbsf->off;
- break;
- }
- case SF_ADDR:
- debug(" addr: bytes:%u rx:%02x ",
- sbsf->addr_bytes, rx[pos]);
- if (sbsf->addr_bytes++ < SF_ADDR_LEN)
- sbsf->off = (sbsf->off << 8) | rx[pos];
- debug("addr:%06x\n", sbsf->off);
- sb_spi_tristate(&tx[pos++], 1);
- /* See if we're done processing */
- if (sbsf->addr_bytes < SF_ADDR_LEN + sbsf->pad_addr_bytes)
- break;
- /* Next state! */
- os_lseek(sbsf->fd, sbsf->off, OS_SEEK_SET);
check error return
- switch (sbsf->cmd) {
- case CMD_READ_ARRAY_FAST:
- case CMD_READ_ARRAY_SLOW:
- sbsf->state = SF_READ;
- break;
- case CMD_PAGE_PROGRAM:
- sbsf->state = SF_WRITE;
- break;
- default:
- /* assume erase state ... */
- sbsf->state = SF_ERASE;
- goto case_SF_ERASE;
- }
- debug(" cmd: transition to %s state\n",
- sb_sf_state_name(sbsf->state));
- break;
- case SF_READ:
- /*
- * XXX: need to handle exotic behavior:
- * - reading past end of device
- */
- cnt = bytes - pos;
- debug(" tx: read(%u)\n", cnt);
- pos += os_read(sbsf->fd, tx + pos, cnt);
This can fail (return -1)
- break;
- case SF_READ_STATUS:
- debug(" read status: %#x\n", sbsf->status);
- cnt = bytes - pos;
- memset(tx + pos, sbsf->status, cnt);
- pos += cnt;
- break;
- case SF_WRITE:
- /*
- * XXX: need to handle exotic behavior:
- * - unaligned addresses
- * - more than a page (256) worth of data
- * - reading past end of device
- */
- if (!(sbsf->status & STAT_WEL)) {
- puts("sb_sf: write enable not set before erase\n");
- goto done;
- }
- cnt = bytes - pos;
- debug(" rx: write(%u)\n", cnt);
- sb_spi_tristate(&tx[pos], cnt);
- pos += os_write(sbsf->fd, rx + pos, cnt);
error return
- sbsf->status &= ~STAT_WEL;
- break;
- case SF_ERASE:
- case_SF_ERASE: {
- const struct sb_spi_flash_erase_commands *erase_cmd =
- sbsf->cmd_data;
- if (!(sbsf->status & STAT_WEL)) {
- puts("sb_sf: write enable not set before erase\n");
- goto done;
- }
- /* verify address is aligned */
- if (sbsf->off & ~(erase_cmd->size - 1)) {
- debug(" sector erase: cmd:%#x needs align:%#x, but we got %#x\n",
- erase_cmd->cmd, erase_cmd->size, sbsf->off);
- sbsf->status &= ~STAT_WEL;
- goto done;
- }
- debug(" sector erase addr: %u\n", sbsf->off);
- cnt = bytes - pos;
- sb_spi_tristate(&tx[pos], cnt);
- pos += cnt;
- /* XXX: latch WIP in status, and delay before clearing it ? */
- os_write(sbsf->fd, sb_sf_0xff, erase_cmd->size);
Check error return?
- sbsf->status &= ~STAT_WEL;
- goto done;
- }
- default:
- debug(" ??? no idea what to do ???\n");
- goto done;
- }
- }
- done:
- return pos == bytes ? 0 : 1;
+}
+static const struct sb_spi_emu_ops sb_sf_ops = {
- .setup = sb_sf_setup,
- .free = sb_sf_free,
- .cs_activate = sb_sf_cs_activate,
- .cs_deactivate = sb_sf_cs_deactivate,
- .xfer = sb_sf_xfer,
+};
+static int sb_cmdline_cb_spi_sf(struct sandbox_state *state, const char *arg) +{
- unsigned long bus, cs;
- const char *spec = sb_spi_parse_spec(arg, &bus, &cs);
- if (!spec)
- return 1;
- state->spi[bus][cs][0] = &sb_sf_ops;
- state->spi[bus][cs][1] = spec;
- return 0;
+}
+SB_CMDLINE_OPT(spi_sf, 1, "connect a SPI flash: <bus>:<cs>:<id>:<file>");
1.7.8.5
Regards, Simon

On Thursday 15 March 2012 00:09:59 Simon Glass wrote:
On Mon, Mar 12, 2012 at 8:22 AM, Mike Frysinger wrote:
+/* Used to quickly bulk erase backing store */ +static u8 sb_sf_0xff[0x10000];
Ick, Does it really need to be so large?
in order to do a single write() for a single sector, yes. it also simplifies the code as i don't have to loop over some smaller size and keep track of how many bytes i've written until a sector is cleared.
this is in the bss and it's 64KiB. i'm really not worried about your development system running Chrome getting low on RAM :).
sbsf->fd = os_open(file, 02);
if (sbsf->fd == -1) {
free(sbsf);
goto error;
Prints incorrect error if the file couldn't be found/open. I wonder if we should have os_perror() ?
probably would be useful. we prob have to document that while os_perror() would work, strerror(errno) would not. the os_* funcs update the errno that the C library knows about (or *__errno_location() in glibc), but u-boot declares a local "errno" variable. so attempting to reference "errno" in u- boot code will always resolve to the local definition rather than the glibc one. os_perror() would work because it calls __errno_location() internally just like the other os_* funcs.
pos += os_read(sbsf->fd, tx + pos, cnt);
This can fail (return -1)
the ways in which read/write could fail aren't really possible for us, but i guess it won't hurt to assert() the values. -mike

Hi Mike,
On Wed, Mar 14, 2012 at 10:24 PM, Mike Frysinger vapier@gentoo.org wrote:
On Thursday 15 March 2012 00:09:59 Simon Glass wrote:
On Mon, Mar 12, 2012 at 8:22 AM, Mike Frysinger wrote:
+/* Used to quickly bulk erase backing store */ +static u8 sb_sf_0xff[0x10000];
Ick, Does it really need to be so large?
in order to do a single write() for a single sector, yes. it also simplifies the code as i don't have to loop over some smaller size and keep track of how many bytes i've written until a sector is cleared.
this is in the bss and it's 64KiB. i'm really not worried about your development system running Chrome getting low on RAM :).
No, it's more that I think it is a bit grubby. But OK.
- sbsf->fd = os_open(file, 02);
- if (sbsf->fd == -1) {
- free(sbsf);
- goto error;
Prints incorrect error if the file couldn't be found/open. I wonder if we should have os_perror() ?
probably would be useful. we prob have to document that while os_perror() would work, strerror(errno) would not. the os_* funcs update the errno that the C library knows about (or *__errno_location() in glibc), but u-boot declares a local "errno" variable. so attempting to reference "errno" in u- boot code will always resolve to the local definition rather than the glibc one. os_perror() would work because it calls __errno_location() internally just like the other os_* funcs.
Yes a bit messy, let's leave it for now then.
- pos += os_read(sbsf->fd, tx + pos, cnt);
This can fail (return -1)
the ways in which read/write could fail aren't really possible for us, but i guess it won't hurt to assert() the values.
Are you sure? What if the file has a read error? That could happen in the test setup. To me an assert is a bit ugly for this sort of thing. Also for test code we don't want it dying in the bowels of the test with an assert - better to return test failure so people can see what went wrong.
Regards, Simon
-mike

We want to test SPI flash code in the sandbox, so enable the new drivers.
Acked-by: Simon Glass sjg@chromium.org Signed-off-by: Mike Frysinger vapier@gentoo.org --- v3 - no changes wrt v2
include/configs/sandbox.h | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h index a58a34e..236acdf 100644 --- a/include/configs/sandbox.h +++ b/include/configs/sandbox.h @@ -61,6 +61,13 @@ #define CONFIG_ENV_SIZE 8192 #define CONFIG_ENV_IS_NOWHERE
+/* SPI */ +#define CONFIG_SANDBOX_SPI +#define CONFIG_CMD_SF +#define CONFIG_SPI_FLASH +#define CONFIG_SPI_FLASH_SANDBOX +#define CONFIG_SPI_FLASH_STMICRO + #define CONFIG_SYS_HZ 1000
/* Memory things - we don't really want a memory test */
participants (2)
-
Mike Frysinger
-
Simon Glass