X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=drivers%2Fmtd%2Fspi%2Fsandbox.c;h=4944059fa3f16c1d8710089f18e92246854bea3b;hb=475bf816f1fbc7be5c5ba12cd456841a0f9508c9;hp=a62ef4cbbd39024052d53d37c731fba29d3fe5a2;hpb=215ab45a67672a7c1ceb347f7b74c94323415222;p=oweals%2Fu-boot.git diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c index a62ef4cbbd..4944059fa3 100644 --- a/drivers/mtd/spi/sandbox.c +++ b/drivers/mtd/spi/sandbox.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -19,6 +20,11 @@ #include #include #include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; /* * The different states that our SPI flash transitions between. @@ -34,12 +40,14 @@ enum sandbox_sf_state { SF_ERASE, /* erase the flash */ SF_READ_STATUS, /* read the flash's status register */ SF_READ_STATUS1, /* read the flash's status register upper 8 bits*/ + SF_WRITE_STATUS, /* write the flash's status register */ }; static const char *sandbox_sf_state_name(enum sandbox_sf_state state) { static const char * const states[] = { "CMD", "ID", "ADDR", "READ", "WRITE", "ERASE", "READ_STATUS", + "READ_STATUS1", "WRITE_STATUS", }; return states[state]; } @@ -51,52 +59,14 @@ static const char *sandbox_sf_state_name(enum sandbox_sf_state state) /* Assume all SPI flashes have 3 byte addresses since they do atm */ #define SF_ADDR_LEN 3 -struct sandbox_spi_flash_erase_commands { - u8 cmd; - u32 size; -}; -#define IDCODE_LEN 5 -#define MAX_ERASE_CMDS 3 -struct sandbox_spi_flash_data { - const char *name; - u8 idcode[IDCODE_LEN]; - u32 size; - const struct sandbox_spi_flash_erase_commands - erase_cmds[MAX_ERASE_CMDS]; -}; - -/* Structure describing all the flashes we know how to emulate */ -static const struct sandbox_spi_flash_data sandbox_sf_flashes[] = { - { - "M25P16", { 0x20, 0x20, 0x15 }, (2 << 20), - { /* erase commands */ - { 0xd8, (64 << 10), }, /* sector */ - { 0xc7, (2 << 20), }, /* bulk */ - }, - }, - { - "W25Q32", { 0xef, 0x40, 0x16 }, (4 << 20), - { /* erase commands */ - { 0x20, (4 << 10), }, /* 4KB */ - { 0xd8, (64 << 10), }, /* sector */ - { 0xc7, (4 << 20), }, /* bulk */ - }, - }, - { - "W25Q128", { 0xef, 0x40, 0x18 }, (16 << 20), - { /* erase commands */ - { 0x20, (4 << 10), }, /* 4KB */ - { 0xd8, (64 << 10), }, /* sector */ - { 0xc7, (16 << 20), }, /* bulk */ - }, - }, -}; +#define IDCODE_LEN 3 /* Used to quickly bulk erase backing store */ static u8 sandbox_sf_0xff[0x1000]; /* Internal state data for each SPI flash */ struct sandbox_spi_flash { + unsigned int cs; /* Chip select we are attached to */ /* * As we receive data over the SPI bus, our flash transitions * between states. For example, we start off in the SF_CMD @@ -109,7 +79,8 @@ struct sandbox_spi_flash { */ enum sandbox_sf_state state; uint cmd; - const void *cmd_data; + /* Erase size of current erase command */ + uint erase_size; /* Current position in the flash; used when reading/writing/etc... */ uint off; /* How many address bytes we've consumed */ @@ -117,77 +88,133 @@ struct sandbox_spi_flash { /* The current flash status (see STAT_XXX defines above) */ u16 status; /* Data describing the flash we're emulating */ - const struct sandbox_spi_flash_data *data; + const struct spi_flash_info *data; /* The file on disk to serv up data from */ int fd; }; -static int sandbox_sf_setup(void **priv, const char *spec) +struct sandbox_spi_flash_plat_data { + const char *filename; + const char *device_name; + int bus; + int cs; +}; + +/** + * This is a very strange probe function. If it has platform data (which may + * have come from the device tree) then this function gets the filename and + * device type from there. Failing that it looks at the command line + * parameter. + */ +static int sandbox_sf_probe(struct udevice *dev) { /* spec = idcode:file */ - struct sandbox_spi_flash *sbsf; + struct sandbox_spi_flash *sbsf = dev_get_priv(dev); const char *file; - size_t i, len, idname_len; - const struct sandbox_spi_flash_data *data; + size_t len, idname_len; + const struct spi_flash_info *data; + struct sandbox_spi_flash_plat_data *pdata = dev_get_platdata(dev); + struct sandbox_state *state = state_get_current(); + struct udevice *bus = dev->parent; + const char *spec = NULL; + int ret = 0; + int cs = -1; + int i; + + debug("%s: bus %d, looking for emul=%p: ", __func__, bus->seq, dev); + if (bus->seq >= 0 && bus->seq < CONFIG_SANDBOX_SPI_MAX_BUS) { + for (i = 0; i < CONFIG_SANDBOX_SPI_MAX_CS; i++) { + if (state->spi[bus->seq][i].emul == dev) + cs = i; + } + } + if (cs == -1) { + printf("Error: Unknown chip select for device '%s'\n", + dev->name); + return -EINVAL; + } + debug("found at cs %d\n", cs); + + if (!pdata->filename) { + struct sandbox_state *state = state_get_current(); + + assert(bus->seq != -1); + if (bus->seq < CONFIG_SANDBOX_SPI_MAX_BUS) + spec = state->spi[bus->seq][cs].spec; + if (!spec) { + debug("%s: No spec found for bus %d, cs %d\n", + __func__, bus->seq, cs); + ret = -ENOENT; + goto error; + } - file = strchr(spec, ':'); - if (!file) { - printf("sandbox_sf: unable to parse file\n"); - goto error; + file = strchr(spec, ':'); + if (!file) { + printf("%s: unable to parse file\n", __func__); + ret = -EINVAL; + goto error; + } + idname_len = file - spec; + pdata->filename = file + 1; + pdata->device_name = spec; + ++file; + } else { + spec = strchr(pdata->device_name, ','); + if (spec) + spec++; + else + spec = pdata->device_name; + idname_len = strlen(spec); } - idname_len = file - spec; - ++file; + debug("%s: device='%s'\n", __func__, spec); - for (i = 0; i < ARRAY_SIZE(sandbox_sf_flashes); ++i) { - data = &sandbox_sf_flashes[i]; + for (data = spi_flash_ids; data->name; data++) { len = strlen(data->name); if (idname_len != len) continue; - if (!memcmp(spec, data->name, len)) + if (!strncasecmp(spec, data->name, len)) break; } - if (i == ARRAY_SIZE(sandbox_sf_flashes)) { - printf("sandbox_sf: unknown flash '%*s'\n", (int)idname_len, + if (!data->name) { + printf("%s: unknown flash '%*s'\n", __func__, (int)idname_len, spec); + ret = -EINVAL; goto error; } if (sandbox_sf_0xff[0] == 0x00) memset(sandbox_sf_0xff, 0xff, sizeof(sandbox_sf_0xff)); - sbsf = calloc(sizeof(*sbsf), 1); - if (!sbsf) { - printf("sandbox_sf: out of memory\n"); - goto error; - } - - sbsf->fd = os_open(file, 02); + sbsf->fd = os_open(pdata->filename, 02); if (sbsf->fd == -1) { - free(sbsf); - printf("sandbox_sf: unable to open file '%s'\n", file); + printf("%s: unable to open file '%s'\n", __func__, + pdata->filename); + ret = -EIO; goto error; } sbsf->data = data; + sbsf->cs = cs; - *priv = sbsf; return 0; error: - return 1; + debug("%s: Got error %d\n", __func__, ret); + return ret; } -static void sandbox_sf_free(void *priv) +static int sandbox_sf_remove(struct udevice *dev) { - struct sandbox_spi_flash *sbsf = priv; + struct sandbox_spi_flash *sbsf = dev_get_priv(dev); os_close(sbsf->fd); - free(sbsf); + + return 0; } -static void sandbox_sf_cs_activate(void *priv) +static void sandbox_sf_cs_activate(struct udevice *dev) { - struct sandbox_spi_flash *sbsf = priv; + struct sandbox_spi_flash *sbsf = dev_get_priv(dev); debug("sandbox_sf: CS activated; state is fresh!\n"); @@ -199,11 +226,24 @@ static void sandbox_sf_cs_activate(void *priv) sbsf->cmd = SF_CMD; } -static void sandbox_sf_cs_deactivate(void *priv) +static void sandbox_sf_cs_deactivate(struct udevice *dev) { debug("sandbox_sf: CS deactivated; cmd done processing!\n"); } +/* + * 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 void sandbox_spi_tristate(u8 *buf, uint len) +{ + /* XXX: make this into a user config option ? */ + memset(buf, 0xff, len); +} + /* Figure out what command this stream is telling us to do */ static int sandbox_sf_process_cmd(struct sandbox_spi_flash *sbsf, const u8 *rx, u8 *tx) @@ -211,7 +251,8 @@ static int sandbox_sf_process_cmd(struct sandbox_spi_flash *sbsf, const u8 *rx, enum sandbox_sf_state oldstate = sbsf->state; /* We need to output a byte for the cmd byte we just ate */ - sandbox_spi_tristate(tx, 1); + if (tx) + sandbox_spi_tristate(tx, 1); sbsf->cmd = rx[0]; switch (sbsf->cmd) { @@ -223,7 +264,6 @@ static int sandbox_sf_process_cmd(struct sandbox_spi_flash *sbsf, const u8 *rx, 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: @@ -240,25 +280,26 @@ static int sandbox_sf_process_cmd(struct sandbox_spi_flash *sbsf, const u8 *rx, debug(" write enabled\n"); sbsf->status |= STAT_WEL; break; + case CMD_WRITE_STATUS: + sbsf->state = SF_WRITE_STATUS; + break; default: { - size_t i; - - /* handle erase commands first */ - for (i = 0; i < MAX_ERASE_CMDS; ++i) { - const struct sandbox_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; + int flags = sbsf->data->flags; + + /* we only support erase here */ + if (sbsf->cmd == CMD_ERASE_CHIP) { + sbsf->erase_size = sbsf->data->sector_size * + sbsf->data->n_sectors; + } else if (sbsf->cmd == CMD_ERASE_4K && (flags & SECT_4K)) { + sbsf->erase_size = 4 << 10; + } else if (sbsf->cmd == CMD_ERASE_64K && !(flags & SECT_4K)) { + sbsf->erase_size = 64 << 10; + } else { + debug(" cmd unknown: %#x\n", sbsf->cmd); + return -EIO; } - - debug(" cmd unknown: %#x\n", sbsf->cmd); - return 1; + sbsf->state = SF_ADDR; + break; } } @@ -275,7 +316,7 @@ int sandbox_erase_part(struct sandbox_spi_flash *sbsf, int size) int ret; while (size > 0) { - todo = min(size, sizeof(sandbox_sf_0xff)); + todo = min(size, (int)sizeof(sandbox_sf_0xff)); ret = os_write(sbsf->fd, sandbox_sf_0xff, todo); if (ret != todo) return ret; @@ -285,20 +326,27 @@ int sandbox_erase_part(struct sandbox_spi_flash *sbsf, int size) return 0; } -static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx, - uint bytes) +static int sandbox_sf_xfer(struct udevice *dev, unsigned int bitlen, + const void *rxp, void *txp, unsigned long flags) { - struct sandbox_spi_flash *sbsf = priv; + struct sandbox_spi_flash *sbsf = dev_get_priv(dev); + const uint8_t *rx = rxp; + uint8_t *tx = txp; uint cnt, pos = 0; + int bytes = bitlen / 8; int ret; debug("sandbox_sf: state:%x(%s) bytes:%u\n", sbsf->state, sandbox_sf_state_name(sbsf->state), bytes); + if ((flags & SPI_XFER_BEGIN)) + sandbox_sf_cs_activate(dev); + if (sbsf->state == SF_CMD) { /* Figure out the initial state */ - if (sandbox_sf_process_cmd(sbsf, rx, tx)) - return 1; + ret = sandbox_sf_process_cmd(sbsf, rx, tx); + if (ret) + return ret; ++pos; } @@ -309,11 +357,15 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx, u8 id; debug(" id: off:%u tx:", sbsf->off); - if (sbsf->off < IDCODE_LEN) - id = sbsf->data->idcode[sbsf->off]; - else + if (sbsf->off < IDCODE_LEN) { + /* Extract correct byte from ID 0x00aabbcc */ + id = ((JEDEC_MFR(sbsf->data) << 16) | + JEDEC_ID(sbsf->data)) >> + (8 * (IDCODE_LEN - 1 - sbsf->off)); + } else { id = 0; - debug("%02x\n", id); + } + debug("%d %02x\n", sbsf->off, id); tx[pos++] = id; ++sbsf->off; break; @@ -326,7 +378,9 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx, sbsf->off = (sbsf->off << 8) | rx[pos]; debug("addr:%06x\n", sbsf->off); - sandbox_spi_tristate(&tx[pos++], 1); + if (tx) + sandbox_spi_tristate(&tx[pos], 1); + pos++; /* See if we're done processing */ if (sbsf->addr_bytes < @@ -336,7 +390,7 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx, /* Next state! */ if (os_lseek(sbsf->fd, sbsf->off, OS_SEEK_SET) < 0) { puts("sandbox_sf: os_lseek() failed"); - return 1; + return -EIO; } switch (sbsf->cmd) { case CMD_READ_ARRAY_FAST: @@ -362,10 +416,11 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx, cnt = bytes - pos; debug(" tx: read(%u)\n", cnt); + assert(tx); ret = os_read(sbsf->fd, tx + pos, cnt); if (ret < 0) { - puts("sandbox_spi: os_read() failed\n"); - return 1; + puts("sandbox_sf: os_read() failed\n"); + return -EIO; } pos += ret; break; @@ -381,6 +436,10 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx, memset(tx + pos, sbsf->status >> 8, cnt); pos += cnt; break; + case SF_WRITE_STATUS: + debug(" write status: %#x (ignored)\n", rx[pos]); + pos = bytes; + break; case SF_WRITE: /* * XXX: need to handle exotic behavior: @@ -395,45 +454,45 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx, cnt = bytes - pos; debug(" rx: write(%u)\n", cnt); - sandbox_spi_tristate(&tx[pos], cnt); + if (tx) + sandbox_spi_tristate(&tx[pos], cnt); ret = os_write(sbsf->fd, rx + pos, cnt); if (ret < 0) { puts("sandbox_spi: os_write() failed\n"); - return 1; + return -EIO; } pos += ret; sbsf->status &= ~STAT_WEL; break; case SF_ERASE: case_sf_erase: { - const struct sandbox_spi_flash_erase_commands * - erase_cmd = sbsf->cmd_data; - if (!(sbsf->status & STAT_WEL)) { puts("sandbox_sf: write enable not set before erase\n"); goto done; } /* verify address is aligned */ - if (sbsf->off & (erase_cmd->size - 1)) { + if (sbsf->off & (sbsf->erase_size - 1)) { debug(" sector erase: cmd:%#x needs align:%#x, but we got %#x\n", - erase_cmd->cmd, erase_cmd->size, + sbsf->cmd, sbsf->erase_size, sbsf->off); sbsf->status &= ~STAT_WEL; goto done; } - debug(" sector erase addr: %u\n", sbsf->off); + debug(" sector erase addr: %u, size: %u\n", sbsf->off, + sbsf->erase_size); cnt = bytes - pos; - sandbox_spi_tristate(&tx[pos], cnt); + if (tx) + sandbox_spi_tristate(&tx[pos], cnt); pos += cnt; /* * TODO(vapier@gentoo.org): latch WIP in status, and * delay before clearing it ? */ - ret = sandbox_erase_part(sbsf, erase_cmd->size); + ret = sandbox_erase_part(sbsf, sbsf->erase_size); sbsf->status &= ~STAT_WEL; if (ret) { debug("sandbox_sf: Erase failed\n"); @@ -448,17 +507,33 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx, } done: - return pos == bytes ? 0 : 1; + if (flags & SPI_XFER_END) + sandbox_sf_cs_deactivate(dev); + return pos == bytes ? 0 : -EIO; } -static const struct sandbox_spi_emu_ops sandbox_sf_ops = { - .setup = sandbox_sf_setup, - .free = sandbox_sf_free, - .cs_activate = sandbox_sf_cs_activate, - .cs_deactivate = sandbox_sf_cs_deactivate, +int sandbox_sf_ofdata_to_platdata(struct udevice *dev) +{ + struct sandbox_spi_flash_plat_data *pdata = dev_get_platdata(dev); + const void *blob = gd->fdt_blob; + int node = dev->of_offset; + + pdata->filename = fdt_getprop(blob, node, "sandbox,filename", NULL); + pdata->device_name = fdt_getprop(blob, node, "compatible", NULL); + if (!pdata->filename || !pdata->device_name) { + debug("%s: Missing properties, filename=%s, device_name=%s\n", + __func__, pdata->filename, pdata->device_name); + return -EINVAL; + } + + return 0; +} + +static const struct dm_spi_emul_ops sandbox_sf_emul_ops = { .xfer = sandbox_sf_xfer, }; +#ifdef CONFIG_SPI_FLASH static int sandbox_cmdline_cb_spi_sf(struct sandbox_state *state, const char *arg) { @@ -476,8 +551,151 @@ static int sandbox_cmdline_cb_spi_sf(struct sandbox_state *state, * spec here, but the problem is that no U-Boot init has been done * yet. Perhaps we can figure something out. */ - state->spi[bus][cs].ops = &sandbox_sf_ops; state->spi[bus][cs].spec = spec; + debug("%s: Setting up spec '%s' for bus %ld, cs %ld\n", __func__, + spec, bus, cs); + return 0; } SANDBOX_CMDLINE_OPT(spi_sf, 1, "connect a SPI flash: :::"); + +int sandbox_sf_bind_emul(struct sandbox_state *state, int busnum, int cs, + struct udevice *bus, int of_offset, const char *spec) +{ + struct udevice *emul; + char name[20], *str; + struct driver *drv; + int ret; + + /* now the emulator */ + strncpy(name, spec, sizeof(name) - 6); + name[sizeof(name) - 6] = '\0'; + strcat(name, "-emul"); + str = strdup(name); + if (!str) + return -ENOMEM; + drv = lists_driver_lookup_name("sandbox_sf_emul"); + if (!drv) { + puts("Cannot find sandbox_sf_emul driver\n"); + return -ENOENT; + } + ret = device_bind(bus, drv, str, NULL, of_offset, &emul); + if (ret) { + printf("Cannot create emul device for spec '%s' (err=%d)\n", + spec, ret); + return ret; + } + state->spi[busnum][cs].emul = emul; + + return 0; +} + +void sandbox_sf_unbind_emul(struct sandbox_state *state, int busnum, int cs) +{ + struct udevice *dev; + + dev = state->spi[busnum][cs].emul; + device_remove(dev); + device_unbind(dev); + state->spi[busnum][cs].emul = NULL; +} + +static int sandbox_sf_bind_bus_cs(struct sandbox_state *state, int busnum, + int cs, const char *spec) +{ + struct udevice *bus, *slave; + int ret; + + ret = uclass_find_device_by_seq(UCLASS_SPI, busnum, true, &bus); + if (ret) { + printf("Invalid bus %d for spec '%s' (err=%d)\n", busnum, + spec, ret); + return ret; + } + ret = spi_find_chip_select(bus, cs, &slave); + if (!ret) { + printf("Chip select %d already exists for spec '%s'\n", cs, + spec); + return -EEXIST; + } + + ret = device_bind_driver(bus, "spi_flash_std", spec, &slave); + if (ret) + return ret; + + return sandbox_sf_bind_emul(state, busnum, cs, bus, -1, spec); +} + +int sandbox_spi_get_emul(struct sandbox_state *state, + struct udevice *bus, struct udevice *slave, + struct udevice **emulp) +{ + struct sandbox_spi_info *info; + int busnum = bus->seq; + int cs = spi_chip_select(slave); + int ret; + + info = &state->spi[busnum][cs]; + if (!info->emul) { + /* Use the same device tree node as the SPI flash device */ + debug("%s: busnum=%u, cs=%u: binding SPI flash emulation: ", + __func__, busnum, cs); + ret = sandbox_sf_bind_emul(state, busnum, cs, bus, + slave->of_offset, slave->name); + if (ret) { + debug("failed (err=%d)\n", ret); + return ret; + } + debug("OK\n"); + } + *emulp = info->emul; + + return 0; +} + +int dm_scan_other(bool pre_reloc_only) +{ + struct sandbox_state *state = state_get_current(); + int busnum, cs; + + if (pre_reloc_only) + return 0; + for (busnum = 0; busnum < CONFIG_SANDBOX_SPI_MAX_BUS; busnum++) { + for (cs = 0; cs < CONFIG_SANDBOX_SPI_MAX_CS; cs++) { + const char *spec = state->spi[busnum][cs].spec; + int ret; + + if (spec) { + ret = sandbox_sf_bind_bus_cs(state, busnum, + cs, spec); + if (ret) { + debug("%s: Bind failed for bus %d, cs %d\n", + __func__, busnum, cs); + return ret; + } + debug("%s: Setting up spec '%s' for bus %d, cs %d\n", + __func__, spec, busnum, cs); + } + } + } + + return 0; +} +#endif + +static const struct udevice_id sandbox_sf_ids[] = { + { .compatible = "sandbox,spi-flash" }, + { } +}; + +U_BOOT_DRIVER(sandbox_sf_emul) = { + .name = "sandbox_sf_emul", + .id = UCLASS_SPI_EMUL, + .of_match = sandbox_sf_ids, + .ofdata_to_platdata = sandbox_sf_ofdata_to_platdata, + .probe = sandbox_sf_probe, + .remove = sandbox_sf_remove, + .priv_auto_alloc_size = sizeof(struct sandbox_spi_flash), + .platdata_auto_alloc_size = sizeof(struct sandbox_spi_flash_plat_data), + .ops = &sandbox_sf_emul_ops, +};