diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/misc/cros_ec_spi.c | 3 | ||||
| -rw-r--r-- | drivers/mtd/spi/Makefile | 1 | ||||
| -rw-r--r-- | drivers/mtd/spi/sandbox.c | 483 | ||||
| -rw-r--r-- | drivers/mtd/spi/sf_internal.h | 1 | ||||
| -rw-r--r-- | drivers/mtd/spi/sf_probe.c | 28 | ||||
| -rw-r--r-- | drivers/spi/Makefile | 1 | ||||
| -rw-r--r-- | drivers/spi/exynos_spi.c | 10 | ||||
| -rw-r--r-- | drivers/spi/sandbox_spi.c | 204 | ||||
| -rw-r--r-- | drivers/spi/spi.c | 19 | 
9 files changed, 738 insertions, 12 deletions
| diff --git a/drivers/misc/cros_ec_spi.c b/drivers/misc/cros_ec_spi.c index 202acf258..2fc911025 100644 --- a/drivers/misc/cros_ec_spi.c +++ b/drivers/misc/cros_ec_spi.c @@ -135,8 +135,7 @@ int cros_ec_spi_decode_fdt(struct cros_ec_dev *dev, const void *blob)   */  int cros_ec_spi_init(struct cros_ec_dev *dev, const void *blob)  { -	dev->spi = spi_setup_slave_fdt(blob, dev->parent_node, -				       dev->cs, dev->max_frequency, 0); +	dev->spi = spi_setup_slave_fdt(blob, dev->parent_node, dev->node);  	if (!dev->spi) {  		debug("%s: Could not setup SPI slave\n", __func__);  		return -1; diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 1bbeb7da3..26483a23f 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -13,4 +13,5 @@ endif  obj-$(CONFIG_CMD_SF)        += sf.o  obj-$(CONFIG_SPI_FLASH) += sf_probe.o sf_ops.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/sandbox.c b/drivers/mtd/spi/sandbox.c new file mode 100644 index 000000000..a62ef4cbb --- /dev/null +++ b/drivers/mtd/spi/sandbox.c @@ -0,0 +1,483 @@ +/* + * Simulate a SPI flash + * + * Copyright (c) 2011-2013 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 "sf_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. + */ +enum sandbox_sf_state { +	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 */ +	SF_READ_STATUS1, /* read the flash's status register upper 8 bits*/ +}; + +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", +	}; +	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 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 */ +		}, +	}, +}; + +/* Used to quickly bulk erase backing store */ +static u8 sandbox_sf_0xff[0x1000]; + +/* Internal state data for each SPI flash */ +struct sandbox_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. +	 */ +	enum sandbox_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) */ +	u16 status; +	/* Data describing the flash we're emulating */ +	const struct sandbox_spi_flash_data *data; +	/* The file on disk to serv up data from */ +	int fd; +}; + +static int sandbox_sf_setup(void **priv, const char *spec) +{ +	/* spec = idcode:file */ +	struct sandbox_spi_flash *sbsf; +	const char *file; +	size_t i, len, idname_len; +	const struct sandbox_spi_flash_data *data; + +	file = strchr(spec, ':'); +	if (!file) { +		printf("sandbox_sf: unable to parse file\n"); +		goto error; +	} +	idname_len = file - spec; +	++file; + +	for (i = 0; i < ARRAY_SIZE(sandbox_sf_flashes); ++i) { +		data = &sandbox_sf_flashes[i]; +		len = strlen(data->name); +		if (idname_len != len) +			continue; +		if (!memcmp(spec, data->name, len)) +			break; +	} +	if (i == ARRAY_SIZE(sandbox_sf_flashes)) { +		printf("sandbox_sf: unknown flash '%*s'\n", (int)idname_len, +		       spec); +		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); +	if (sbsf->fd == -1) { +		free(sbsf); +		printf("sandbox_sf: unable to open file '%s'\n", file); +		goto error; +	} + +	sbsf->data = data; + +	*priv = sbsf; +	return 0; + + error: +	return 1; +} + +static void sandbox_sf_free(void *priv) +{ +	struct sandbox_spi_flash *sbsf = priv; + +	os_close(sbsf->fd); +	free(sbsf); +} + +static void sandbox_sf_cs_activate(void *priv) +{ +	struct sandbox_spi_flash *sbsf = priv; + +	debug("sandbox_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 = SF_CMD; +	sbsf->cmd = SF_CMD; +} + +static void sandbox_sf_cs_deactivate(void *priv) +{ +	debug("sandbox_sf: CS deactivated; cmd done processing!\n"); +} + +/* 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) +{ +	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); + +	sbsf->cmd = rx[0]; +	switch (sbsf->cmd) { +	case CMD_READ_ID: +		sbsf->state = SF_ID; +		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_READ_STATUS1: +		sbsf->state = SF_READ_STATUS1; +		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 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; +		} + +		debug(" cmd unknown: %#x\n", sbsf->cmd); +		return 1; +	} +	} + +	if (oldstate != sbsf->state) +		debug(" cmd: transition to %s state\n", +		      sandbox_sf_state_name(sbsf->state)); + +	return 0; +} + +int sandbox_erase_part(struct sandbox_spi_flash *sbsf, int size) +{ +	int todo; +	int ret; + +	while (size > 0) { +		todo = min(size, sizeof(sandbox_sf_0xff)); +		ret = os_write(sbsf->fd, sandbox_sf_0xff, todo); +		if (ret != todo) +			return ret; +		size -= todo; +	} + +	return 0; +} + +static int sandbox_sf_xfer(void *priv, const u8 *rx, u8 *tx, +		uint bytes) +{ +	struct sandbox_spi_flash *sbsf = priv; +	uint cnt, pos = 0; +	int ret; + +	debug("sandbox_sf: state:%x(%s) bytes:%u\n", sbsf->state, +	      sandbox_sf_state_name(sbsf->state), bytes); + +	if (sbsf->state == SF_CMD) { +		/* Figure out the initial state */ +		if (sandbox_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); + +			sandbox_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! */ +			if (os_lseek(sbsf->fd, sbsf->off, OS_SEEK_SET) < 0) { +				puts("sandbox_sf: os_lseek() failed"); +				return 1; +			} +			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", +			      sandbox_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); +			ret = os_read(sbsf->fd, tx + pos, cnt); +			if (ret < 0) { +				puts("sandbox_spi: os_read() failed\n"); +				return 1; +			} +			pos += ret; +			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_READ_STATUS1: +			debug(" read status: %#x\n", sbsf->status); +			cnt = bytes - pos; +			memset(tx + pos, sbsf->status >> 8, 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("sandbox_sf: write enable not set before write\n"); +				goto done; +			} + +			cnt = bytes - pos; +			debug(" rx: write(%u)\n", cnt); +			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; +			} +			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)) { +				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; +			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); +			sbsf->status &= ~STAT_WEL; +			if (ret) { +				debug("sandbox_sf: Erase failed\n"); +				goto done; +			} +			goto done; +		} +		default: +			debug(" ??? no idea what to do ???\n"); +			goto done; +		} +	} + + done: +	return pos == bytes ? 0 : 1; +} + +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, +	.xfer          = sandbox_sf_xfer, +}; + +static int sandbox_cmdline_cb_spi_sf(struct sandbox_state *state, +				     const char *arg) +{ +	unsigned long bus, cs; +	const char *spec = sandbox_spi_parse_spec(arg, &bus, &cs); + +	if (!spec) +		return 1; + +	/* +	 * 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: <bus>:<cs>:<id>:<file>"); diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 732ddf836..d291746ed 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -28,6 +28,7 @@  #define CMD_PAGE_PROGRAM		0x02  #define CMD_WRITE_DISABLE		0x04  #define CMD_READ_STATUS			0x05 +#define CMD_READ_STATUS1		0x35  #define CMD_WRITE_ENABLE		0x06  #define CMD_READ_CONFIG			0x35  #define CMD_FLAG_STATUS			0x70 diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c index 5eb8ffe84..c1eb75489 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -13,6 +13,7 @@  #include <malloc.h>  #include <spi.h>  #include <spi_flash.h> +#include <asm/io.h>  #include "sf_internal.h" @@ -279,22 +280,19 @@ int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash)  		debug("%s: Memory map must cover entire device\n", __func__);  		return -1;  	} -	flash->memory_map = (void *)addr; +	flash->memory_map = map_sysmem(addr, size);  	return 0;  }  #endif /* CONFIG_OF_CONTROL */ -struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, -		unsigned int max_hz, unsigned int spi_mode) +static struct spi_flash *spi_flash_probe_slave(struct spi_slave *spi)  { -	struct spi_slave *spi;  	struct spi_flash *flash = NULL;  	u8 idcode[5];  	int ret;  	/* Setup spi_slave */ -	spi = spi_setup_slave(bus, cs, max_hz, spi_mode);  	if (!spi) {  		printf("SF: Failed to set up slave\n");  		return NULL; @@ -358,6 +356,26 @@ err_claim_bus:  	return NULL;  } +struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, +		unsigned int max_hz, unsigned int spi_mode) +{ +	struct spi_slave *spi; + +	spi = spi_setup_slave(bus, cs, max_hz, spi_mode); +	return spi_flash_probe_slave(spi); +} + +#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; + +	spi = spi_setup_slave_fdt(blob, slave_node, spi_node); +	return spi_flash_probe_slave(spi); +} +#endif +  void spi_flash_free(struct spi_flash *flash)  {  	spi_free_slave(flash->spi); diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 27902fe39..ed4ecd754 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_MXC_SPI) += mxc_spi.o  obj-$(CONFIG_MXS_SPI) += mxs_spi.o  obj-$(CONFIG_OC_TINY_SPI) += oc_tiny_spi.o  obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o +obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o  obj-$(CONFIG_SOFT_SPI) += soft_spi.o  obj-$(CONFIG_SH_SPI) += sh_spi.o  obj-$(CONFIG_FSL_ESPI) += fsl_espi.o diff --git a/drivers/spi/exynos_spi.c b/drivers/spi/exynos_spi.c index 699c57eb6..4d5def2d3 100644 --- a/drivers/spi/exynos_spi.c +++ b/drivers/spi/exynos_spi.c @@ -529,18 +529,18 @@ static int process_nodes(const void *blob, int node_list[], int count)   * @param node		SPI peripheral node to use   * @return 0 if ok, -1 on error   */ -struct spi_slave *spi_setup_slave_fdt(const void *blob, int node, -		unsigned int cs, unsigned int max_hz, unsigned int mode) +struct spi_slave *spi_setup_slave_fdt(const void *blob, int slave_node, +				      int spi_node)  {  	struct spi_bus *bus;  	unsigned int i;  	for (i = 0, bus = spi_bus; i < bus_count; i++, bus++) { -		if (bus->node == node) -			return spi_setup_slave(i, cs, max_hz, mode); +		if (bus->node == spi_node) +			return spi_base_setup_slave_fdt(blob, i, slave_node);  	} -	debug("%s: Failed to find bus node %d\n", __func__, node); +	debug("%s: Failed to find bus node %d\n", __func__, spi_node);  	return NULL;  } diff --git a/drivers/spi/sandbox_spi.c b/drivers/spi/sandbox_spi.c new file mode 100644 index 000000000..7895305a0 --- /dev/null +++ b/drivers/spi/sandbox_spi.c @@ -0,0 +1,204 @@ +/* + * Simulate a SPI port + * + * Copyright (c) 2011-2013 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/errno.h> +#include <asm/spi.h> +#include <asm/state.h> + +#ifndef CONFIG_SPI_IDLE_VAL +# define CONFIG_SPI_IDLE_VAL 0xFF +#endif + +struct sandbox_spi_slave { +	struct spi_slave slave; +	const struct sandbox_spi_emu_ops *ops; +	void *priv; +}; + +#define to_sandbox_spi_slave(s) container_of(s, struct sandbox_spi_slave, slave) + +const char *sandbox_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 sandbox_spi_slave *sss = to_sandbox_spi_slave(slave); + +	debug("sandbox_spi: activating CS\n"); +	if (sss->ops->cs_activate) +		sss->ops->cs_activate(sss->priv); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ +	struct sandbox_spi_slave *sss = to_sandbox_spi_slave(slave); + +	debug("sandbox_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 sandbox_spi_slave *sss; +	struct sandbox_state *state = state_get_current(); +	const char *spec; + +	if (!spi_cs_is_valid(bus, cs)) { +		debug("sandbox_spi: Invalid SPI bus/cs\n"); +		return NULL; +	} + +	sss = spi_alloc_slave(struct sandbox_spi_slave, bus, cs); +	if (!sss) { +		debug("sandbox_spi: Out of memory\n"); +		return NULL; +	} + +	spec = state->spi[bus][cs].spec; +	sss->ops = state->spi[bus][cs].ops; +	if (!spec || !sss->ops || sss->ops->setup(&sss->priv, spec)) { +		free(sss); +		printf("sandbox_spi: unable to locate a slave client\n"); +		return NULL; +	} + +	return &sss->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ +	struct sandbox_spi_slave *sss = to_sandbox_spi_slave(slave); + +	debug("sandbox_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("sandbox_spi: error: bus already claimed: %d!\n", +		       spi_bus_claim_cnt[slave->bus]); +	} + +	return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ +	if (--spi_bus_claim_cnt[slave->bus]) { +		printf("sandbox_spi: error: bus freed too often: %d!\n", +		       spi_bus_claim_cnt[slave->bus]); +	} +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, +		void *din, unsigned long flags) +{ +	struct sandbox_spi_slave *sss = to_sandbox_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("sandbox_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("sandbox_spi: xfer: auto-allocating tx scratch buffer\n"); +		tx = malloc(bytes); +		if (!tx) { +			debug("sandbox_spi: Out of memory\n"); +			return -ENOMEM; +		} +	} +	if (!rx) { +		debug("sandbox_spi: xfer: auto-allocating rx scratch buffer\n"); +		rx = malloc(bytes); +		if (!rx) { +			debug("sandbox_spi: Out of memory\n"); +			return -ENOMEM; +		} +	} + +	debug("sandbox_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("sandbox_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; +} diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b76a26cef..7ddea9b02 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -5,6 +5,7 @@   */  #include <common.h> +#include <fdtdec.h>  #include <malloc.h>  #include <spi.h> @@ -37,3 +38,21 @@ void *spi_do_alloc_slave(int offset, int size, unsigned int bus,  	return ptr;  } + +#ifdef CONFIG_OF_SPI +struct spi_slave *spi_base_setup_slave_fdt(const void *blob, int busnum, +					   int node) +{ +	int cs, max_hz, mode = 0; + +	cs = fdtdec_get_int(blob, node, "reg", -1); +	max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", 100000); +	if (fdtdec_get_bool(blob, node, "spi-cpol")) +		mode |= SPI_CPOL; +	if (fdtdec_get_bool(blob, node, "spi-cpha")) +		mode |= SPI_CPHA; +	if (fdtdec_get_bool(blob, node, "spi-cs-high")) +		mode |= SPI_CS_HIGH; +	return spi_setup_slave(busnum, cs, max_hz, mode); +} +#endif |