Commit 176b4377 authored by Lee Jones's avatar Lee Jones Committed by Brian Norris
Browse files

mtd: st_spi_fsm: Supply a busy wait for post-write status



When we write data to the Serial Flash chip we'll wait a predetermined
period of time before giving up. During that period of time we poll the
status register until completion.

Acked-by Angus Clark <angus.clark@st.com>
Signed-off-by: default avatarLee Jones <lee.jones@linaro.org>
Signed-off-by: default avatarBrian Norris <computersforpeace@gmail.com>
parent 30ca64f9
Loading
Loading
Loading
Loading
+221 −0
Original line number Diff line number Diff line
@@ -238,8 +238,18 @@
#define FLASH_CMD_READ4_1_1_4  0x6c
#define FLASH_CMD_READ4_1_4_4  0xec

/* Status register */
#define FLASH_STATUS_BUSY      0x01
#define FLASH_STATUS_WEL       0x02
#define FLASH_STATUS_BP0       0x04
#define FLASH_STATUS_BP1       0x08
#define FLASH_STATUS_BP2       0x10
#define FLASH_STATUS_SRWP0     0x80
#define FLASH_STATUS_TIMEOUT   0xff

#define FLASH_PAGESIZE         256			/* In Bytes    */
#define FLASH_PAGESIZE_32      (FLASH_PAGESIZE / 4)	/* In uint32_t */
#define FLASH_MAX_BUSY_WAIT    (300 * HZ)	/* Maximum 'CHIPERASE' time */

/*
 * Flags to tweak operation of default read/write/erase routines
@@ -519,6 +529,22 @@ static struct stfsm_seq stfsm_seq_read_jedec = {
		    SEQ_CFG_STARTSEQ),
};

static struct stfsm_seq stfsm_seq_read_status_fifo = {
	.data_size = TRANSFER_SIZE(4),
	.seq_opc[0] = (SEQ_OPC_PADS_1 |
		       SEQ_OPC_CYCLES(8) |
		       SEQ_OPC_OPCODE(FLASH_CMD_RDSR)),
	.seq = {
		STFSM_INST_CMD1,
		STFSM_INST_DATA_READ,
		STFSM_INST_STOP,
	},
	.seq_cfg = (SEQ_CFG_PADS_1 |
		    SEQ_CFG_READNOTWRITE |
		    SEQ_CFG_CSDEASSERT |
		    SEQ_CFG_STARTSEQ),
};

static struct stfsm_seq stfsm_seq_erase_sector = {
	/* 'addr_cfg' configured during initialisation */
	.seq_opc = {
@@ -699,6 +725,53 @@ static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter)
	return 0;
}

static uint8_t stfsm_wait_busy(struct stfsm *fsm)
{
	struct stfsm_seq *seq = &stfsm_seq_read_status_fifo;
	unsigned long deadline;
	uint32_t status;
	int timeout = 0;

	/* Use RDRS1 */
	seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
			   SEQ_OPC_CYCLES(8) |
			   SEQ_OPC_OPCODE(FLASH_CMD_RDSR));

	/* Load read_status sequence */
	stfsm_load_seq(fsm, seq);

	/*
	 * Repeat until busy bit is deasserted, or timeout, or error (S25FLxxxS)
	 */
	deadline = jiffies + FLASH_MAX_BUSY_WAIT;
	while (!timeout) {
		cond_resched();

		if (time_after_eq(jiffies, deadline))
			timeout = 1;

		stfsm_wait_seq(fsm);

		stfsm_read_fifo(fsm, &status, 4);

		if ((status & FLASH_STATUS_BUSY) == 0)
			return 0;

		if ((fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS) &&
		    ((status & S25FL_STATUS_P_ERR) ||
		     (status & S25FL_STATUS_E_ERR)))
			return (uint8_t)(status & 0xff);

		if (!timeout)
			/* Restart */
			writel(seq->seq_cfg, fsm->base + SPI_FAST_SEQ_CFG);
	}

	dev_err(fsm->dev, "timeout on wait_busy\n");

	return FLASH_STATUS_TIMEOUT;
}

static int stfsm_wrvcr(struct stfsm *fsm, uint8_t data)
{
	struct stfsm_seq *seq = &stfsm_seq_wrvcr;
@@ -1020,6 +1093,98 @@ static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size,
	return 0;
}

static int stfsm_write(struct stfsm *fsm, const uint8_t *const buf,
		       const uint32_t size, const uint32_t offset)
{
	struct stfsm_seq *seq = &stfsm_seq_write;
	uint32_t data_pads;
	uint32_t write_mask;
	uint32_t size_ub;
	uint32_t size_lb;
	uint32_t size_mop;
	uint32_t tmp[4];
	uint32_t page_buf[FLASH_PAGESIZE_32];
	uint8_t *t = (uint8_t *)&tmp;
	const uint8_t *p;
	int ret;
	int i;

	dev_dbg(fsm->dev, "writing %d bytes to 0x%08x\n", size, offset);

	/* Enter 32-bit address mode, if required */
	if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR)
		stfsm_enter_32bit_addr(fsm, 1);

	/* Must write in multiples of 32 cycles (or 32*pads/8 bytes) */
	data_pads = ((seq->seq_cfg >> 16) & 0x3) + 1;
	write_mask = (data_pads << 2) - 1;

	/* Handle non-aligned buf */
	if ((uint32_t)buf & 0x3) {
		memcpy(page_buf, buf, size);
		p = (uint8_t *)page_buf;
	} else {
		p = buf;
	}

	/* Handle non-aligned size */
	size_ub = (size + write_mask) & ~write_mask;
	size_lb = size & ~write_mask;
	size_mop = size & write_mask;

	seq->data_size = TRANSFER_SIZE(size_ub);
	seq->addr1 = (offset >> 16) & 0xffff;
	seq->addr2 = offset & 0xffff;

	/* Need to set FIFO to write mode, before writing data to FIFO (see
	 * GNBvb79594)
	 */
	writel(0x00040000, fsm->base + SPI_FAST_SEQ_CFG);

	/*
	 * Before writing data to the FIFO, apply a small delay to allow a
	 * potential change of FIFO direction to complete.
	 */
	if (fsm->fifo_dir_delay == 0)
		readl(fsm->base + SPI_FAST_SEQ_CFG);
	else
		udelay(fsm->fifo_dir_delay);


	/* Write data to FIFO, before starting sequence (see GNBvd79593) */
	if (size_lb) {
		stfsm_write_fifo(fsm, (uint32_t *)p, size_lb);
		p += size_lb;
	}

	/* Handle non-aligned size */
	if (size_mop) {
		memset(t, 0xff, write_mask + 1);	/* fill with 0xff's */
		for (i = 0; i < size_mop; i++)
			t[i] = *p++;

		stfsm_write_fifo(fsm, tmp, write_mask + 1);
	}

	/* Start sequence */
	stfsm_load_seq(fsm, seq);

	/* Wait for sequence to finish */
	stfsm_wait_seq(fsm);

	/* Wait for completion */
	ret = stfsm_wait_busy(fsm);

	/* Exit 32-bit address mode, if required */
	if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR) {
		stfsm_enter_32bit_addr(fsm, 0);
		if (fsm->configuration & CFG_WRITE_EX_32BIT_ADDR_DELAY)
			udelay(1);
	}

	return 0;
}

/*
 * Read an address range from the flash chip. The address range
 * may be any size provided it is within the physical boundaries.
@@ -1052,6 +1217,61 @@ static int stfsm_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
	return 0;
}

/*
 * Write an address range to the flash chip.  Data must be written in
 * FLASH_PAGESIZE chunks.  The address range may be any size provided
 * it is within the physical boundaries.
 */
static int stfsm_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
			   size_t *retlen, const u_char *buf)
{
	struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent);

	u32 page_offs;
	u32 bytes;
	uint8_t *b = (uint8_t *)buf;
	int ret = 0;

	dev_dbg(fsm->dev, "%s to 0x%08x, len %zd\n", __func__, (u32)to, len);

	*retlen = 0;

	if (!len)
		return 0;

	if (to + len > mtd->size)
		return -EINVAL;

	/* Offset within page */
	page_offs = to % FLASH_PAGESIZE;

	mutex_lock(&fsm->lock);

	while (len) {
		/* Write up to page boundary */
		bytes = min(FLASH_PAGESIZE - page_offs, len);

		ret = stfsm_write(fsm, b, bytes, to);
		if (ret)
			goto out1;

		b += bytes;
		len -= bytes;
		to += bytes;

		/* We are now page-aligned */
		page_offs = 0;

		*retlen += bytes;

	}

out1:
	mutex_unlock(&fsm->lock);

	return ret;
}

static void stfsm_read_jedec(struct stfsm *fsm, uint8_t *const jedec)
{
	const struct stfsm_seq *seq = &stfsm_seq_read_jedec;
@@ -1308,6 +1528,7 @@ static int stfsm_probe(struct platform_device *pdev)
	fsm->mtd.erasesize	= info->sector_size;

	fsm->mtd._read  = stfsm_mtd_read;
	fsm->mtd._write = stfsm_mtd_write;

	dev_err(&pdev->dev,
		"Found serial flash device: %s\n"