X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=drivers%2Fmtd%2Fspi%2Fsandbox.c;h=084c66e9840b43c06b8b8b2a14182d0011a577b6;hb=b3192f48c19c15d37ba69722b2846de4b73b27cd;hp=98e0a34d4e3b2fa74017fff981b80f47d0736271;hpb=3cc83f9d08a80fddf4c1e8e766eff8273f30814c;p=oweals%2Fu-boot.git diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c index 98e0a34d4e..084c66e984 100644 --- a/drivers/mtd/spi/sandbox.c +++ b/drivers/mtd/spi/sandbox.c @@ -8,7 +8,10 @@ * Licensed under the GPL-2 or later. */ +#define LOG_CATEGORY UCLASS_SPI_FLASH + #include +#include #include #include #include @@ -19,6 +22,9 @@ #include #include #include +#include +#include +#include /* * The different states that our SPI flash transitions between. @@ -34,19 +40,25 @@ 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 */ }; +#if CONFIG_IS_ENABLED(LOG) 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]; } +#endif /* LOG */ /* Bits for the status register */ #define STAT_WIP (1 << 0) #define STAT_WEL (1 << 1) +#define STAT_BP_SHIFT 2 +#define STAT_BP_MASK (7 << STAT_BP_SHIFT) /* Assume all SPI flashes have 3 byte addresses since they do atm */ #define SF_ADDR_LEN 3 @@ -58,6 +70,7 @@ 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 @@ -79,78 +92,118 @@ struct sandbox_spi_flash { /* The current flash status (see STAT_XXX defines above) */ u16 status; /* Data describing the flash we're emulating */ - const struct spi_flash_params *data; + const struct 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; +}; + +void sandbox_sf_set_block_protect(struct udevice *dev, int bp_mask) +{ + struct sandbox_spi_flash *sbsf = dev_get_priv(dev); + + sbsf->status &= ~STAT_BP_MASK; + sbsf->status |= bp_mask << STAT_BP_SHIFT; +} + +/** + * 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. + */ +static int sandbox_sf_probe(struct udevice *dev) { /* spec = idcode:file */ - struct sandbox_spi_flash *sbsf; - const char *file; + struct sandbox_spi_flash *sbsf = dev_get_priv(dev); size_t len, idname_len; - const struct spi_flash_params *data; - - file = strchr(spec, ':'); - if (!file) { - printf("sandbox_sf: unable to parse file\n"); - goto error; + const struct flash_info *data; + struct sandbox_spi_flash_plat_data *pdata = dev_get_platdata(dev); + struct sandbox_state *state = state_get_current(); + struct dm_spi_slave_platdata *slave_plat; + struct udevice *bus = dev->parent; + const char *spec = NULL; + struct udevice *emul; + int ret = 0; + int cs = -1; + + debug("%s: bus %d, looking for emul=%p: ", __func__, bus->seq, dev); + ret = sandbox_spi_get_emul(state, bus, dev, &emul); + if (ret) { + printf("Error: Unknown chip select for device '%s'\n", + dev->name); + return ret; } - idname_len = file - spec; - ++file; + slave_plat = dev_get_parent_platdata(dev); + cs = slave_plat->cs; + debug("found at cs %d\n", cs); - for (data = spi_flash_params_table; data->name; data++) { + if (!pdata->filename) { + printf("Error: No filename available\n"); + return -EINVAL; + } + spec = strchr(pdata->device_name, ','); + if (spec) + spec++; + else + spec = pdata->device_name; + idname_len = strlen(spec); + debug("%s: device='%s'\n", __func__, spec); + + for (data = spi_nor_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 (!data->name) { - printf("sandbox_sf: unknown flash '%*s'\n", (int)idname_len, + 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"); + log_content("sandbox_sf: CS activated; state is fresh!\n"); /* CS is asserted, so reset state */ sbsf->off = 0; @@ -160,9 +213,22 @@ 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) +{ + log_content("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) { - debug("sandbox_sf: CS deactivated; cmd done processing!\n"); + /* XXX: make this into a user config option ? */ + memset(buf, 0xff, len); } /* Figure out what command this stream is telling us to do */ @@ -172,51 +238,52 @@ 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) { - case CMD_READ_ID: + case SPINOR_OP_RDID: sbsf->state = SF_ID; sbsf->cmd = SF_ID; break; - case CMD_READ_ARRAY_FAST: + case SPINOR_OP_READ_FAST: sbsf->pad_addr_bytes = 1; - case CMD_READ_ARRAY_SLOW: - case CMD_PAGE_PROGRAM: + case SPINOR_OP_READ: + case SPINOR_OP_PP: sbsf->state = SF_ADDR; break; - case CMD_WRITE_DISABLE: + case SPINOR_OP_WRDI: debug(" write disabled\n"); sbsf->status &= ~STAT_WEL; break; - case CMD_READ_STATUS: + case SPINOR_OP_RDSR: sbsf->state = SF_READ_STATUS; break; - case CMD_READ_STATUS1: + case SPINOR_OP_RDSR2: sbsf->state = SF_READ_STATUS1; break; - case CMD_WRITE_ENABLE: + case SPINOR_OP_WREN: debug(" write enabled\n"); sbsf->status |= STAT_WEL; break; + case SPINOR_OP_WRSR: + sbsf->state = SF_WRITE_STATUS; + break; default: { int flags = sbsf->data->flags; /* we only support erase here */ - if (sbsf->cmd == CMD_ERASE_CHIP) { + if (sbsf->cmd == SPINOR_OP_CHIP_ERASE) { sbsf->erase_size = sbsf->data->sector_size * - sbsf->data->nr_sectors; - } else if (sbsf->cmd == CMD_ERASE_4K && (flags & SECT_4K)) { + sbsf->data->n_sectors; + } else if (sbsf->cmd == SPINOR_OP_BE_4K && (flags & SECT_4K)) { sbsf->erase_size = 4 << 10; - } else if (sbsf->cmd == CMD_ERASE_32K && (flags & SECT_32K)) { - sbsf->erase_size = 32 << 10; - } else if (sbsf->cmd == CMD_ERASE_64K && - !(flags & (SECT_4K | SECT_32K))) { + } else if (sbsf->cmd == SPINOR_OP_SE && !(flags & SECT_4K)) { sbsf->erase_size = 64 << 10; } else { debug(" cmd unknown: %#x\n", sbsf->cmd); - return 1; + return -EIO; } sbsf->state = SF_ADDR; break; @@ -224,8 +291,8 @@ static int sandbox_sf_process_cmd(struct sandbox_spi_flash *sbsf, const u8 *rx, } if (oldstate != sbsf->state) - debug(" cmd: transition to %s state\n", - sandbox_sf_state_name(sbsf->state)); + log_content(" cmd: transition to %s state\n", + sandbox_sf_state_name(sbsf->state)); return 0; } @@ -236,7 +303,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; @@ -246,20 +313,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); + log_content("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; } @@ -269,28 +343,31 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx, case SF_ID: { u8 id; - debug(" id: off:%u tx:", sbsf->off); + log_content(" id: off:%u tx:", sbsf->off); if (sbsf->off < IDCODE_LEN) { /* Extract correct byte from ID 0x00aabbcc */ - id = sbsf->data->jedec >> + id = ((JEDEC_MFR(sbsf->data) << 16) | + JEDEC_ID(sbsf->data)) >> (8 * (IDCODE_LEN - 1 - sbsf->off)); } else { id = 0; } - debug("%d %02x\n", sbsf->off, id); + log_content("%d %02x\n", sbsf->off, id); tx[pos++] = id; ++sbsf->off; break; } case SF_ADDR: - debug(" addr: bytes:%u rx:%02x ", sbsf->addr_bytes, - rx[pos]); + log_content(" 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); + log_content("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 < @@ -300,14 +377,14 @@ 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: - case CMD_READ_ARRAY_SLOW: + case SPINOR_OP_READ_FAST: + case SPINOR_OP_READ: sbsf->state = SF_READ; break; - case CMD_PAGE_PROGRAM: + case SPINOR_OP_PP: sbsf->state = SF_WRITE; break; default: @@ -315,8 +392,8 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx, sbsf->state = SF_ERASE; goto case_sf_erase; } - debug(" cmd: transition to %s state\n", - sandbox_sf_state_name(sbsf->state)); + log_content(" cmd: transition to %s state\n", + sandbox_sf_state_name(sbsf->state)); break; case SF_READ: /* @@ -325,26 +402,31 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx, */ cnt = bytes - pos; - debug(" tx: read(%u)\n", cnt); + log_content(" 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; case SF_READ_STATUS: - debug(" read status: %#x\n", sbsf->status); + log_content(" read status: %#x\n", sbsf->status); cnt = bytes - pos; memset(tx + pos, sbsf->status, cnt); pos += cnt; break; case SF_READ_STATUS1: - debug(" read status: %#x\n", sbsf->status); + log_content(" read status: %#x\n", sbsf->status); cnt = bytes - pos; memset(tx + pos, sbsf->status >> 8, cnt); pos += cnt; break; + case SF_WRITE_STATUS: + log_content(" write status: %#x (ignored)\n", rx[pos]); + pos = bytes; + break; case SF_WRITE: /* * XXX: need to handle exotic behavior: @@ -358,12 +440,13 @@ 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); + log_content(" rx: write(%u)\n", 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; @@ -377,18 +460,19 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx, /* verify address is aligned */ if (sbsf->off & (sbsf->erase_size - 1)) { - debug(" sector erase: cmd:%#x needs align:%#x, but we got %#x\n", - sbsf->cmd, sbsf->erase_size, - sbsf->off); + log_content(" sector erase: cmd:%#x needs align:%#x, but we got %#x\n", + sbsf->cmd, sbsf->erase_size, + sbsf->off); sbsf->status &= ~STAT_WEL; goto done; } - debug(" sector erase addr: %u, size: %u\n", sbsf->off, - sbsf->erase_size); + log_content(" 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; /* @@ -398,48 +482,126 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx, ret = sandbox_erase_part(sbsf, sbsf->erase_size); sbsf->status &= ~STAT_WEL; if (ret) { - debug("sandbox_sf: Erase failed\n"); + log_content("sandbox_sf: Erase failed\n"); goto done; } goto done; } default: - debug(" ??? no idea what to do ???\n"); + log_content(" ??? no idea what to do ???\n"); goto done; } } done: - return pos == bytes ? 0 : 1; + if (flags & SPI_XFER_END) + sandbox_sf_cs_deactivate(dev); + return pos == bytes ? 0 : -EIO; +} + +int sandbox_sf_ofdata_to_platdata(struct udevice *dev) +{ + struct sandbox_spi_flash_plat_data *pdata = dev_get_platdata(dev); + + pdata->filename = dev_read_string(dev, "sandbox,filename"); + pdata->device_name = dev_read_string(dev, "compatible"); + 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 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, +static const struct dm_spi_emul_ops sandbox_sf_emul_ops = { .xfer = sandbox_sf_xfer, }; -static int sandbox_cmdline_cb_spi_sf(struct sandbox_state *state, - const char *arg) +#ifdef CONFIG_SPI_FLASH +int sandbox_sf_bind_emul(struct sandbox_state *state, int busnum, int cs, + struct udevice *bus, ofnode node, const char *spec) { - unsigned long bus, cs; - const char *spec = sandbox_spi_parse_spec(arg, &bus, &cs); + struct udevice *emul; + char name[20], *str; + struct driver *drv; + int ret; - if (!spec) - return 1; + /* now the emulator */ + strncpy(name, spec, sizeof(name) - 6); + name[sizeof(name) - 6] = '\0'; + strcat(name, "-emul"); + drv = lists_driver_lookup_name("sandbox_sf_emul"); + if (!drv) { + puts("Cannot find sandbox_sf_emul driver\n"); + return -ENOENT; + } + str = strdup(name); + if (!str) + return -ENOMEM; + ret = device_bind_ofnode(bus, drv, str, NULL, node, &emul); + if (ret) { + free(str); + printf("Cannot create emul device for spec '%s' (err=%d)\n", + spec, ret); + return ret; + } + state->spi[busnum][cs].emul = emul; - /* - * It is safe to not make a copy of 'spec' because it comes from the - * command line. - * - * TODO(sjg@chromium.org): It would be nice if we could parse the - * 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; return 0; } -SANDBOX_CMDLINE_OPT(spi_sf, 1, "connect a SPI flash: :::"); + +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, DM_REMOVE_NORMAL); + device_unbind(dev); + state->spi[busnum][cs].emul = NULL; +} + +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, + dev_ofnode(slave), slave->name); + if (ret) { + debug("failed (err=%d)\n", ret); + return ret; + } + debug("OK\n"); + } + *emulp = info->emul; + + 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, +};