From 843c9e8796a7a6ebe0c9dec54c6a64ac29949d8f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 13 Oct 2014 23:41:55 -0600 Subject: dm: Add spi.h header to a few files Some files are using SPI functions but not explitly including the SPI header file. Fix this, since driver model needs it. Signed-off-by: Simon Glass Reviewed-by: Jagannadha Sutradharudu Teki --- drivers/mtd/spi/sf_params.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd/spi') diff --git a/drivers/mtd/spi/sf_params.c b/drivers/mtd/spi/sf_params.c index 453edf0..61545ca 100644 --- a/drivers/mtd/spi/sf_params.c +++ b/drivers/mtd/spi/sf_params.c @@ -7,6 +7,7 @@ */ #include +#include #include #include "sf_internal.h" -- cgit v1.1 From ff0960f9a018952cbaab59c0d87b6e0338c8f04c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 13 Oct 2014 23:42:04 -0600 Subject: sf: Tidy up public and private header files Since spi_flash.h is supposed to be the public API for SPI flash, move private things to sf_internal.h. Also tidy up a few comment nits. Signed-off-by: Simon Glass Reviewed-by: Jagannadha Sutradharudu Teki --- drivers/mtd/spi/ramtron.c | 1 + drivers/mtd/spi/sf_internal.h | 67 +++++++++++++++++++++++++++++++++++++----- drivers/mtd/spi/spi_spl_load.c | 1 + 3 files changed, 62 insertions(+), 7 deletions(-) (limited to 'drivers/mtd/spi') diff --git a/drivers/mtd/spi/ramtron.c b/drivers/mtd/spi/ramtron.c index d50da37..a23032c 100644 --- a/drivers/mtd/spi/ramtron.c +++ b/drivers/mtd/spi/ramtron.c @@ -35,6 +35,7 @@ #include #include +#include #include #include "sf_internal.h" diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 19d4914..5b7670c 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -10,6 +10,36 @@ #ifndef _SF_INTERNAL_H_ #define _SF_INTERNAL_H_ +#include +#include + +/* Dual SPI flash memories - see SPI_COMM_DUAL_... */ +enum spi_dual_flash { + SF_SINGLE_FLASH = 0, + SF_DUAL_STACKED_FLASH = 1 << 0, + SF_DUAL_PARALLEL_FLASH = 1 << 1, +}; + +/* Enum list - Full read commands */ +enum spi_read_cmds { + ARRAY_SLOW = 1 << 0, + DUAL_OUTPUT_FAST = 1 << 1, + DUAL_IO_FAST = 1 << 2, + QUAD_OUTPUT_FAST = 1 << 3, + QUAD_IO_FAST = 1 << 4, +}; + +#define RD_EXTN (ARRAY_SLOW | DUAL_OUTPUT_FAST | DUAL_IO_FAST) +#define RD_FULL (RD_EXTN | QUAD_OUTPUT_FAST | QUAD_IO_FAST) + +/* sf param flags */ +enum { + SECT_4K = 1 << 0, + SECT_32K = 1 << 1, + E_FSR = 1 << 2, + WR_QPP = 1 << 3, +}; + #define SPI_FLASH_3B_ADDR_LEN 3 #define SPI_FLASH_CMD_LEN (1 + SPI_FLASH_3B_ADDR_LEN) #define SPI_FLASH_16MB_BOUN 0x1000000 @@ -30,12 +60,12 @@ #define CMD_WRITE_STATUS 0x01 #define CMD_PAGE_PROGRAM 0x02 #define CMD_WRITE_DISABLE 0x04 -#define CMD_READ_STATUS 0x05 +#define CMD_READ_STATUS 0x05 #define CMD_QUAD_PAGE_PROGRAM 0x32 #define CMD_READ_STATUS1 0x35 #define CMD_WRITE_ENABLE 0x06 -#define CMD_READ_CONFIG 0x35 -#define CMD_FLAG_STATUS 0x70 +#define CMD_READ_CONFIG 0x35 +#define CMD_FLAG_STATUS 0x70 /* Read commands */ #define CMD_READ_ARRAY_SLOW 0x03 @@ -57,7 +87,7 @@ /* Common status */ #define STATUS_WIP (1 << 0) #define STATUS_QEB_WINSPAN (1 << 1) -#define STATUS_QEB_MXIC (1 << 6) +#define STATUS_QEB_MXIC (1 << 6) #define STATUS_PEC (1 << 7) #ifdef CONFIG_SYS_SPI_ST_ENABLE_WP_PIN @@ -66,19 +96,42 @@ /* Flash timeout values */ #define SPI_FLASH_PROG_TIMEOUT (2 * CONFIG_SYS_HZ) -#define SPI_FLASH_PAGE_ERASE_TIMEOUT (5 * CONFIG_SYS_HZ) +#define SPI_FLASH_PAGE_ERASE_TIMEOUT (5 * CONFIG_SYS_HZ) #define SPI_FLASH_SECTOR_ERASE_TIMEOUT (10 * CONFIG_SYS_HZ) /* SST specific */ #ifdef CONFIG_SPI_FLASH_SST -# define SST_WP 0x01 /* Supports AAI word program */ +# define SST_WP 0x01 /* Supports AAI word program */ # define CMD_SST_BP 0x02 /* Byte Program */ -# define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */ +# define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */ int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, const void *buf); #endif +/** + * struct spi_flash_params - SPI/QSPI flash device params structure + * + * @name: Device name ([MANUFLETTER][DEVTYPE][DENSITY][EXTRAINFO]) + * @jedec: Device jedec ID (0x[1byte_manuf_id][2byte_dev_id]) + * @ext_jedec: Device ext_jedec ID + * @sector_size: Sector size of this device + * @nr_sectors: No.of sectors on this device + * @e_rd_cmd: Enum list for read commands + * @flags: Important param, for flash specific behaviour + */ +struct spi_flash_params { + const char *name; + u32 jedec; + u16 ext_jedec; + u32 sector_size; + u32 nr_sectors; + u8 e_rd_cmd; + u16 flags; +}; + +extern const struct spi_flash_params spi_flash_params_table[]; + /* Send a single-byte command to the device and read the response */ int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len); diff --git a/drivers/mtd/spi/spi_spl_load.c b/drivers/mtd/spi/spi_spl_load.c index 59cca0f..2e0c871 100644 --- a/drivers/mtd/spi/spi_spl_load.c +++ b/drivers/mtd/spi/spi_spl_load.c @@ -10,6 +10,7 @@ */ #include +#include #include #include -- cgit v1.1 From ae242cbfd0fc2b9e791688b3d9d6a19900ff3d31 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 13 Oct 2014 23:42:05 -0600 Subject: spi: Use error return value in sf_ops Adjust spi_flash_probe_slave() to return an error value instead of a pointer so we get the correct error return. Have the caller allocate memory for spi_flash to simplify error handling, and also so that driver model can use its existing allocated memory. Add a spi.h include in the sf_params file. Signed-off-by: Simon Glass Reviewed-by: Jagannadha Sutradharudu Teki --- drivers/mtd/spi/sf_probe.c | 90 +++++++++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 33 deletions(-) (limited to 'drivers/mtd/spi') diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c index 4d148d1..212a825 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -95,15 +96,15 @@ static int spi_flash_set_qeb(struct spi_flash *flash, u8 idcode0) } } -static struct spi_flash *spi_flash_validate_params(struct spi_slave *spi, - u8 *idcode) +static int spi_flash_validate_params(struct spi_slave *spi, u8 *idcode, + struct spi_flash *flash) { const struct spi_flash_params *params; - struct spi_flash *flash; u8 cmd; u16 jedec = idcode[1] << 8 | idcode[2]; u16 ext_jedec = idcode[3] << 8 | idcode[4]; + /* Validate params from spi_flash_params table */ params = spi_flash_params_table; for (; params->name != NULL; params++) { if ((params->jedec >> 16) == idcode[0]) { @@ -120,13 +121,7 @@ static struct spi_flash *spi_flash_validate_params(struct spi_slave *spi, printf("SF: Unsupported flash IDs: "); printf("manuf %02x, jedec %04x, ext_jedec %04x\n", idcode[0], jedec, ext_jedec); - return NULL; - } - - flash = calloc(1, sizeof(*flash)); - if (!flash) { - debug("SF: Failed to allocate spi_flash\n"); - return NULL; + return -EPROTONOSUPPORT; } /* Assign spi data */ @@ -227,15 +222,18 @@ static struct spi_flash *spi_flash_validate_params(struct spi_slave *spi, #ifdef CONFIG_SPI_FLASH_BAR u8 curr_bank = 0; if (flash->size > SPI_FLASH_16MB_BOUN) { + int ret; + flash->bank_read_cmd = (idcode[0] == 0x01) ? CMD_BANKADDR_BRRD : CMD_EXTNADDR_RDEAR; flash->bank_write_cmd = (idcode[0] == 0x01) ? CMD_BANKADDR_BRWR : CMD_EXTNADDR_WREAR; - if (spi_flash_read_common(flash, &flash->bank_read_cmd, 1, - &curr_bank, 1)) { + ret = spi_flash_read_common(flash, &flash->bank_read_cmd, 1, + &curr_bank, 1); + if (ret) { debug("SF: fail to read bank addr register\n"); - return NULL; + return ret; } flash->bank_curr = curr_bank; } else { @@ -250,7 +248,7 @@ static struct spi_flash *spi_flash_validate_params(struct spi_slave *spi, spi_flash_cmd_write_status(flash, 0); #endif - return flash; + return 0; } #ifdef CONFIG_OF_CONTROL @@ -309,23 +307,29 @@ static int spi_enable_wp_pin(struct spi_flash *flash) } #endif -static struct spi_flash *spi_flash_probe_slave(struct spi_slave *spi) +/** + * spi_flash_probe_slave() - Probe for a SPI flash device on a bus + * + * @spi: Bus to probe + * @flashp: Pointer to place to put flash info, which may be NULL if the + * space should be allocated + */ +int spi_flash_probe_slave(struct spi_slave *spi, struct spi_flash *flash) { - struct spi_flash *flash = NULL; u8 idcode[5]; int ret; /* Setup spi_slave */ if (!spi) { printf("SF: Failed to set up slave\n"); - return NULL; + return -ENODEV; } /* Claim spi bus */ ret = spi_claim_bus(spi); if (ret) { debug("SF: Failed to claim SPI bus: %d\n", ret); - goto err_claim_bus; + return ret; } /* Read the ID codes */ @@ -340,10 +344,10 @@ static struct spi_flash *spi_flash_probe_slave(struct spi_slave *spi) print_buffer(0, idcode, 1, sizeof(idcode), 0); #endif - /* Validate params from spi_flash_params table */ - flash = spi_flash_validate_params(spi, idcode); - if (!flash) + if (spi_flash_validate_params(spi, idcode, flash)) { + ret = -EINVAL; goto err_read_id; + } /* Set the quad enable bit - only for quad commands */ if ((flash->read_cmd == CMD_READ_QUAD_OUTPUT_FAST) || @@ -351,13 +355,15 @@ static struct spi_flash *spi_flash_probe_slave(struct spi_slave *spi) (flash->write_cmd == CMD_QUAD_PAGE_PROGRAM)) { if (spi_flash_set_qeb(flash, idcode[0])) { debug("SF: Fail to set QEB for %02x\n", idcode[0]); - return NULL; + ret = -EINVAL; + goto err_read_id; } } #ifdef CONFIG_OF_CONTROL if (spi_flash_decode_fdt(gd->fdt_blob, flash)) { debug("SF: FDT decode error\n"); + ret = -EINVAL; goto err_read_id; } #endif @@ -385,32 +391,50 @@ static struct spi_flash *spi_flash_probe_slave(struct spi_slave *spi) /* Release spi bus */ spi_release_bus(spi); - return flash; + return 0; err_read_id: spi_release_bus(spi); -err_claim_bus: - spi_free_slave(spi); - return NULL; + return ret; +} + +static struct spi_flash *spi_flash_probe_tail(struct spi_slave *bus) +{ + struct spi_flash *flash; + + /* Allocate space if needed (not used by sf-uclass */ + flash = calloc(1, sizeof(*flash)); + if (!flash) { + debug("SF: Failed to allocate spi_flash\n"); + return NULL; + } + + if (spi_flash_probe_slave(bus, flash)) { + spi_free_slave(bus); + free(flash); + return NULL; + } + + return flash; } -struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, +struct spi_flash *spi_flash_probe(unsigned int busnum, unsigned int cs, unsigned int max_hz, unsigned int spi_mode) { - struct spi_slave *spi; + struct spi_slave *bus; - spi = spi_setup_slave(bus, cs, max_hz, spi_mode); - return spi_flash_probe_slave(spi); + bus = spi_setup_slave(busnum, cs, max_hz, spi_mode); + return spi_flash_probe_tail(bus); } #ifdef CONFIG_OF_SPI_FLASH struct spi_flash *spi_flash_probe_fdt(const void *blob, int slave_node, int spi_node) { - struct spi_slave *spi; + struct spi_slave *bus; - spi = spi_setup_slave_fdt(blob, slave_node, spi_node); - return spi_flash_probe_slave(spi); + bus = spi_setup_slave_fdt(blob, slave_node, spi_node); + return spi_flash_probe_tail(bus); } #endif -- cgit v1.1 From 4c2dbefde58917205af51a2c20b3069e01e55cf4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 13 Oct 2014 23:42:06 -0600 Subject: dm: sf: Add a uclass for SPI flash Add a driver model uclass for SPI flash which supports the common operations (read, write, erase). Since we must keep support for the non-dm interface, some modification of the spi_flash header is required. CONFIG_DM_SPI_FLASH is used to enable driver model for SPI flash. Signed-off-by: Simon Glass Reviewed-by: Jagannadha Sutradharudu Teki --- drivers/mtd/spi/Makefile | 7 ++++- drivers/mtd/spi/sf-uclass.c | 63 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/spi/sf-uclass.c (limited to 'drivers/mtd/spi') diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 9e18fb4..15789a0 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -5,13 +5,18 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_DM_SPI_FLASH) += sf-uclass.o + ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_SPI_LOAD) += spi_spl_load.o obj-$(CONFIG_SPL_SPI_BOOT) += fsl_espi_spl.o endif +#ifndef CONFIG_DM_SPI +obj-$(CONFIG_SPI_FLASH) += sf_probe.o +#endif obj-$(CONFIG_CMD_SF) += sf.o -obj-$(CONFIG_SPI_FLASH) += sf_params.o sf_probe.o sf_ops.o +obj-$(CONFIG_SPI_FLASH) += sf_ops.o sf_params.o obj-$(CONFIG_SPI_FRAM_RAMTRON) += ramtron.o obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o obj-$(CONFIG_SPI_M95XXX) += eeprom_m95xxx.o diff --git a/drivers/mtd/spi/sf-uclass.c b/drivers/mtd/spi/sf-uclass.c new file mode 100644 index 0000000..376d815 --- /dev/null +++ b/drivers/mtd/spi/sf-uclass.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include "sf_internal.h" + +/* + * TODO(sjg@chromium.org): This is an old-style function. We should remove + * it when all SPI flash drivers use dm + */ +struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int spi_mode) +{ + struct udevice *dev; + + if (spi_flash_probe_bus_cs(bus, cs, max_hz, spi_mode, &dev)) + return NULL; + + return dev->uclass_priv; +} + +void spi_flash_free(struct spi_flash *flash) +{ + spi_flash_remove(flash->spi->dev); +} + +int spi_flash_probe_bus_cs(unsigned int busnum, unsigned int cs, + unsigned int max_hz, unsigned int spi_mode, + struct udevice **devp) +{ + struct spi_slave *slave; + struct udevice *bus; + char name[20], *str; + int ret; + + snprintf(name, sizeof(name), "%d:%d", busnum, cs); + str = strdup(name); + ret = spi_get_bus_and_cs(busnum, cs, max_hz, spi_mode, + "spi_flash_std", str, &bus, &slave); + if (ret) + return ret; + + *devp = slave->dev; + return 0; +} + +int spi_flash_remove(struct udevice *dev) +{ + return device_remove(dev); +} + +UCLASS_DRIVER(spi_flash) = { + .id = UCLASS_SPI_FLASH, + .name = "spi_flash", + .per_device_auto_alloc_size = sizeof(struct spi_flash), +}; -- cgit v1.1 From fbb099183e3a53f77a975964cdf2e73d11e565af Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 13 Oct 2014 23:42:07 -0600 Subject: dm: Convert spi_flash_probe() and 'sf probe' to use driver model We want the SPI flash probing feature to operate as a standard driver. Add a driver for the basic probing feature used by most boards. This will be activated by device_probe() as with any other driver. The 'sf probe' command currently keeps track of the SPI slave that it last used. This doesn't work with driver model, since some other driver or system may have probed the device and have access to it too. On the other hand, if we try to probe a device twice the second probe is a nop with driver model. Fix this by searching for the matching device, removing it, and then probing it again. This should work as expected regardless of other device activity. Signed-off-by: Simon Glass Reviewed-by: Jagannadha Sutradharudu Teki --- drivers/mtd/spi/sf_probe.c | 66 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) (limited to 'drivers/mtd/spi') diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c index 212a825..2636426 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -131,13 +132,15 @@ static int spi_flash_validate_params(struct spi_slave *spi, u8 *idcode, flash->dual_flash = flash->spi->option; /* Assign spi_flash ops */ +#ifndef CONFIG_DM_SPI_FLASH flash->write = spi_flash_cmd_write_ops; -#ifdef CONFIG_SPI_FLASH_SST +#if defined(CONFIG_SPI_FLASH_SST) if (params->flags & SST_WP) flash->write = sst_write_wp; #endif flash->erase = spi_flash_cmd_erase_ops; flash->read = spi_flash_cmd_read_ops; +#endif /* Compute the flash size */ flash->shift = (flash->dual_flash & SF_DUAL_PARALLEL_FLASH) ? 1 : 0; @@ -398,7 +401,8 @@ err_read_id: return ret; } -static struct spi_flash *spi_flash_probe_tail(struct spi_slave *bus) +#ifndef CONFIG_DM_SPI_FLASH +struct spi_flash *spi_flash_probe_tail(struct spi_slave *bus) { struct spi_flash *flash; @@ -443,3 +447,61 @@ void spi_flash_free(struct spi_flash *flash) spi_free_slave(flash->spi); free(flash); } + +#else /* defined CONFIG_DM_SPI_FLASH */ + +static int spi_flash_std_read(struct udevice *dev, u32 offset, size_t len, + void *buf) +{ + struct spi_flash *flash = dev->uclass_priv; + + return spi_flash_cmd_read_ops(flash, offset, len, buf); +} + +int spi_flash_std_write(struct udevice *dev, u32 offset, size_t len, + const void *buf) +{ + struct spi_flash *flash = dev->uclass_priv; + + return spi_flash_cmd_write_ops(flash, offset, len, buf); +} + +int spi_flash_std_erase(struct udevice *dev, u32 offset, size_t len) +{ + struct spi_flash *flash = dev->uclass_priv; + + return spi_flash_cmd_erase_ops(flash, offset, len); +} + +int spi_flash_std_probe(struct udevice *dev) +{ + struct spi_slave *slave = dev_get_parentdata(dev); + struct spi_flash *flash; + + flash = dev->uclass_priv; + flash->dev = dev; + debug("%s: slave=%p, cs=%d\n", __func__, slave, slave->cs); + return spi_flash_probe_slave(slave, flash); +} + +static const struct dm_spi_flash_ops spi_flash_std_ops = { + .read = spi_flash_std_read, + .write = spi_flash_std_write, + .erase = spi_flash_std_erase, +}; + +static const struct udevice_id spi_flash_std_ids[] = { + { .compatible = "spi-flash" }, + { } +}; + +U_BOOT_DRIVER(spi_flash_std) = { + .name = "spi_flash_std", + .id = UCLASS_SPI_FLASH, + .of_match = spi_flash_std_ids, + .probe = spi_flash_std_probe, + .priv_auto_alloc_size = sizeof(struct spi_flash), + .ops = &spi_flash_std_ops, +}; + +#endif /* CONFIG_DM_SPI_FLASH */ -- cgit v1.1 From b6c2956defb42ce7fe1763f628b81e95f485eb2d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 13 Oct 2014 23:42:08 -0600 Subject: dm: sf: sandbox: Convert SPI flash driver to driver model Convert sandbox's spi flash emulation driver to use driver model. Signed-off-by: Simon Glass Reviewed-by: Jagannadha Sutradharudu Teki --- drivers/mtd/spi/sandbox.c | 338 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 291 insertions(+), 47 deletions(-) (limited to 'drivers/mtd/spi') diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c index 98e0a34..1cf2f98 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]; } @@ -58,6 +66,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 @@ -84,71 +93,124 @@ struct sandbox_spi_flash { 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 len, idname_len; const struct spi_flash_params *data; - - file = strchr(spec, ':'); - if (!file) { - printf("sandbox_sf: unable to parse file\n"); - goto error; + 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'", + 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) + return -ENOENT; + + file = strchr(spec, ':'); + if (!file) { + printf("sandbox_sf: unable to parse file\n"); + 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 (data = spi_flash_params_table; 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, 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("sandbox_sf: unable to open file '%s'\n", + pdata->filename); + ret = -EIO; goto error; } sbsf->data = data; + sbsf->cs = cs; - *priv = sbsf; return 0; error: - return 1; + 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"); @@ -160,11 +222,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) @@ -172,7 +247,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) { @@ -200,6 +276,9 @@ 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: { int flags = sbsf->data->flags; @@ -216,7 +295,7 @@ static int sandbox_sf_process_cmd(struct sandbox_spi_flash *sbsf, const u8 *rx, sbsf->erase_size = 64 << 10; } else { debug(" cmd unknown: %#x\n", sbsf->cmd); - return 1; + return -EIO; } sbsf->state = SF_ADDR; break; @@ -246,20 +325,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; } @@ -290,7 +376,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 < @@ -300,7 +388,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: @@ -326,10 +414,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; @@ -345,6 +434,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: @@ -359,11 +452,12 @@ 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; @@ -388,7 +482,8 @@ static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx, sbsf->erase_size); cnt = bytes - pos; - sandbox_spi_tristate(&tx[pos], cnt); + if (tx) + sandbox_spi_tristate(&tx[pos], cnt); pos += cnt; /* @@ -410,17 +505,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; +} + +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 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, }; +#ifdef CONFIG_SPI_FLASH static int sandbox_cmdline_cb_spi_sf(struct sandbox_state *state, const char *arg) { @@ -438,8 +549,141 @@ 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; 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) +{ + 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 = device_find_child_by_seq(bus, cs, true, &slave); + if (!ret) { + printf("Chip select %d already exists for spec '%s'\n", cs, + spec); + return -EEXIST; + } + + ret = spi_bind_device(bus, cs, "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; + } + } + } + } + + 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, +}; -- cgit v1.1