Exemplo n.º 1
0
static int sandbox_sf_xfer(struct udevice *dev, unsigned int bitlen,
			   const void *rxp, void *txp, unsigned long flags)
{
	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 */
		ret = sandbox_sf_process_cmd(sbsf, rx, tx);
		if (ret)
			return ret;
		++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) {
				/* Extract correct byte from ID 0x00aabbcc */
				id = sbsf->data->jedec >>
					(8 * (IDCODE_LEN - 1 - sbsf->off));
			} else {
				id = 0;
			}
			debug("%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]);

			if (sbsf->addr_bytes++ < SF_ADDR_LEN)
				sbsf->off = (sbsf->off << 8) | rx[pos];
			debug("addr:%06x\n", sbsf->off);

			if (tx)
				sandbox_spi_tristate(&tx[pos], 1);
			pos++;

			/* 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 -EIO;
			}
			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);
			assert(tx);
			ret = os_read(sbsf->fd, tx + pos, cnt);
			if (ret < 0) {
				puts("sandbox_sf: os_read() failed\n");
				return -EIO;
			}
			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_STATUS:
			debug(" write status: %#x (ignored)\n", rx[pos]);
			pos = bytes;
			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);
			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 -EIO;
			}
			pos += ret;
			sbsf->status &= ~STAT_WEL;
			break;
		case SF_ERASE:
 case_sf_erase: {
			if (!(sbsf->status & STAT_WEL)) {
				puts("sandbox_sf: write enable not set before erase\n");
				goto done;
			}

			/* 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);
				sbsf->status &= ~STAT_WEL;
				goto done;
			}

			debug(" sector erase addr: %u, size: %u\n", sbsf->off,
			      sbsf->erase_size);

			cnt = bytes - pos;
			if (tx)
				sandbox_spi_tristate(&tx[pos], cnt);
			pos += cnt;

			/*
			 * TODO([email protected]): latch WIP in status, and
			 * delay before clearing it ?
			 */
			ret = sandbox_erase_part(sbsf, sbsf->erase_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;
		}
Exemplo n.º 2
0
/* 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 */
	if (tx)
		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:
		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;
	case CMD_WRITE_STATUS:
		sbsf->state = SF_WRITE_STATUS;
		break;
	default: {
		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->nr_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;
		}
		sbsf->state = SF_ADDR;
		break;
	}
	}

	if (oldstate != sbsf->state)
		debug(" cmd: transition to %s state\n",
		      sandbox_sf_state_name(sbsf->state));

	return 0;
}
Exemplo n.º 3
0
/* 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;
}
Exemplo n.º 4
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([email protected]): 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;
}
Exemplo n.º 5
0
/* 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 */
	if (tx)
		sandbox_spi_tristate(tx, 1);

	sbsf->cmd = rx[0];
	switch (sbsf->cmd) {
	case SPINOR_OP_RDID:
		sbsf->state = SF_ID;
		sbsf->cmd = SF_ID;
		break;
	case SPINOR_OP_READ_FAST:
		sbsf->pad_addr_bytes = 1;
	case SPINOR_OP_READ:
	case SPINOR_OP_PP:
		sbsf->state = SF_ADDR;
		break;
	case SPINOR_OP_WRDI:
		debug(" write disabled\n");
		sbsf->status &= ~STAT_WEL;
		break;
	case SPINOR_OP_RDSR:
		sbsf->state = SF_READ_STATUS;
		break;
	case SPINOR_OP_RDSR2:
		sbsf->state = SF_READ_STATUS1;
		break;
	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 == SPINOR_OP_CHIP_ERASE) {
			sbsf->erase_size = sbsf->data->sector_size *
				sbsf->data->n_sectors;
		} else if (sbsf->cmd == SPINOR_OP_BE_4K && (flags & SECT_4K)) {
			sbsf->erase_size = 4 << 10;
		} else if (sbsf->cmd == SPINOR_OP_SE && !(flags & SECT_4K)) {
			sbsf->erase_size = 64 << 10;
		} else {
			debug(" cmd unknown: %#x\n", sbsf->cmd);
			return -EIO;
		}
		sbsf->state = SF_ADDR;
		break;
	}
	}

	if (oldstate != sbsf->state)
		log_content(" cmd: transition to %s state\n",
			    sandbox_sf_state_name(sbsf->state));

	return 0;
}