Unverified Commit f173f26a authored by Vignesh Raghavendra's avatar Vignesh Raghavendra Committed by Tudor Ambarus
Browse files

mtd: spi-nor: always use bounce buffer for register read/writes



spi-mem layer expects all buffers passed to it to be DMA'able. But
spi-nor layer mostly allocates buffers on stack for reading/writing to
registers and therefore are not DMA'able. Introduce bounce buffer to be
used to read/write to registers. This ensures that buffer passed to
spi-mem layer during register read/writes is DMA'able. With this change
nor->cmd-buf is no longer used, so drop it.

Signed-off-by: default avatarVignesh Raghavendra <vigneshr@ti.com>
Reviewed-by: default avatarBoris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: default avatarTudor Ambarus <tudor.ambarus@microchip.com>
parent 5f9e832c
Loading
Loading
Loading
Loading
+44 −36
Original line number Original line Diff line number Diff line
@@ -296,15 +296,14 @@ struct flash_info {
static int read_sr(struct spi_nor *nor)
static int read_sr(struct spi_nor *nor)
{
{
	int ret;
	int ret;
	u8 val;


	ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val, 1);
	ret = nor->read_reg(nor, SPINOR_OP_RDSR, nor->bouncebuf, 1);
	if (ret < 0) {
	if (ret < 0) {
		pr_err("error %d reading SR\n", (int) ret);
		pr_err("error %d reading SR\n", (int) ret);
		return ret;
		return ret;
	}
	}


	return val;
	return nor->bouncebuf[0];
}
}


/*
/*
@@ -315,15 +314,14 @@ static int read_sr(struct spi_nor *nor)
static int read_fsr(struct spi_nor *nor)
static int read_fsr(struct spi_nor *nor)
{
{
	int ret;
	int ret;
	u8 val;


	ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val, 1);
	ret = nor->read_reg(nor, SPINOR_OP_RDFSR, nor->bouncebuf, 1);
	if (ret < 0) {
	if (ret < 0) {
		pr_err("error %d reading FSR\n", ret);
		pr_err("error %d reading FSR\n", ret);
		return ret;
		return ret;
	}
	}


	return val;
	return nor->bouncebuf[0];
}
}


/*
/*
@@ -334,15 +332,14 @@ static int read_fsr(struct spi_nor *nor)
static int read_cr(struct spi_nor *nor)
static int read_cr(struct spi_nor *nor)
{
{
	int ret;
	int ret;
	u8 val;


	ret = nor->read_reg(nor, SPINOR_OP_RDCR, &val, 1);
	ret = nor->read_reg(nor, SPINOR_OP_RDCR, nor->bouncebuf, 1);
	if (ret < 0) {
	if (ret < 0) {
		dev_err(nor->dev, "error %d reading CR\n", ret);
		dev_err(nor->dev, "error %d reading CR\n", ret);
		return ret;
		return ret;
	}
	}


	return val;
	return nor->bouncebuf[0];
}
}


/*
/*
@@ -351,8 +348,8 @@ static int read_cr(struct spi_nor *nor)
 */
 */
static int write_sr(struct spi_nor *nor, u8 val)
static int write_sr(struct spi_nor *nor, u8 val)
{
{
	nor->cmd_buf[0] = val;
	nor->bouncebuf[0] = val;
	return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1);
	return nor->write_reg(nor, SPINOR_OP_WRSR, nor->bouncebuf, 1);
}
}


/*
/*
@@ -500,31 +497,31 @@ static int set_4byte(struct spi_nor *nor, bool enable)
			 * We must clear the register to enable normal behavior.
			 * We must clear the register to enable normal behavior.
			 */
			 */
			write_enable(nor);
			write_enable(nor);
			nor->cmd_buf[0] = 0;
			nor->bouncebuf[0] = 0;
			nor->write_reg(nor, SPINOR_OP_WREAR, nor->cmd_buf, 1);
			nor->write_reg(nor, SPINOR_OP_WREAR,
				       nor->bouncebuf, 1);
			write_disable(nor);
			write_disable(nor);
		}
		}


		return status;
		return status;
	default:
	default:
		/* Spansion style */
		/* Spansion style */
		nor->cmd_buf[0] = enable << 7;
		nor->bouncebuf[0] = enable << 7;
		return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
		return nor->write_reg(nor, SPINOR_OP_BRWR, nor->bouncebuf, 1);
	}
	}
}
}


static int s3an_sr_ready(struct spi_nor *nor)
static int s3an_sr_ready(struct spi_nor *nor)
{
{
	int ret;
	int ret;
	u8 val;


	ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1);
	ret = nor->read_reg(nor, SPINOR_OP_XRDSR, nor->bouncebuf, 1);
	if (ret < 0) {
	if (ret < 0) {
		dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);
		dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);
		return ret;
		return ret;
	}
	}


	return !!(val & XSR_RDY);
	return !!(nor->bouncebuf[0] & XSR_RDY);
}
}


static int spi_nor_sr_ready(struct spi_nor *nor)
static int spi_nor_sr_ready(struct spi_nor *nor)
@@ -683,7 +680,6 @@ static loff_t spi_nor_s3an_addr_convert(struct spi_nor *nor, unsigned int addr)
 */
 */
static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
{
{
	u8 buf[SPI_NOR_MAX_ADDR_WIDTH];
	int i;
	int i;


	if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
	if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
@@ -697,11 +693,12 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
	 * control
	 * control
	 */
	 */
	for (i = nor->addr_width - 1; i >= 0; i--) {
	for (i = nor->addr_width - 1; i >= 0; i--) {
		buf[i] = addr & 0xff;
		nor->bouncebuf[i] = addr & 0xff;
		addr >>= 8;
		addr >>= 8;
	}
	}


	return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
	return nor->write_reg(nor, nor->erase_opcode, nor->bouncebuf,
			      nor->addr_width);
}
}


/**
/**
@@ -1485,9 +1482,11 @@ static int macronix_quad_enable(struct spi_nor *nor)
 */
 */
static int spansion_quad_enable(struct spi_nor *nor)
static int spansion_quad_enable(struct spi_nor *nor)
{
{
	u8 sr_cr[2] = {0, CR_QUAD_EN_SPAN};
	u8 *sr_cr = nor->bouncebuf;
	int ret;
	int ret;


	sr_cr[0] = 0;
	sr_cr[1] = CR_QUAD_EN_SPAN;
	ret = write_sr_cr(nor, sr_cr);
	ret = write_sr_cr(nor, sr_cr);
	if (ret)
	if (ret)
		return ret;
		return ret;
@@ -1517,7 +1516,7 @@ static int spansion_quad_enable(struct spi_nor *nor)
 */
 */
static int spansion_no_read_cr_quad_enable(struct spi_nor *nor)
static int spansion_no_read_cr_quad_enable(struct spi_nor *nor)
{
{
	u8 sr_cr[2];
	u8 *sr_cr = nor->bouncebuf;
	int ret;
	int ret;


	/* Keep the current value of the Status Register. */
	/* Keep the current value of the Status Register. */
@@ -1548,7 +1547,7 @@ static int spansion_no_read_cr_quad_enable(struct spi_nor *nor)
static int spansion_read_cr_quad_enable(struct spi_nor *nor)
static int spansion_read_cr_quad_enable(struct spi_nor *nor)
{
{
	struct device *dev = nor->dev;
	struct device *dev = nor->dev;
	u8 sr_cr[2];
	u8 *sr_cr = nor->bouncebuf;
	int ret;
	int ret;


	/* Check current Quad Enable bit value. */
	/* Check current Quad Enable bit value. */
@@ -1599,22 +1598,22 @@ static int spansion_read_cr_quad_enable(struct spi_nor *nor)
 */
 */
static int sr2_bit7_quad_enable(struct spi_nor *nor)
static int sr2_bit7_quad_enable(struct spi_nor *nor)
{
{
	u8 sr2;
	u8 *sr2 = nor->bouncebuf;
	int ret;
	int ret;


	/* Check current Quad Enable bit value. */
	/* Check current Quad Enable bit value. */
	ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1);
	ret = nor->read_reg(nor, SPINOR_OP_RDSR2, sr2, 1);
	if (ret)
	if (ret)
		return ret;
		return ret;
	if (sr2 & SR2_QUAD_EN_BIT7)
	if (*sr2 & SR2_QUAD_EN_BIT7)
		return 0;
		return 0;


	/* Update the Quad Enable bit. */
	/* Update the Quad Enable bit. */
	sr2 |= SR2_QUAD_EN_BIT7;
	*sr2 |= SR2_QUAD_EN_BIT7;


	write_enable(nor);
	write_enable(nor);


	ret = nor->write_reg(nor, SPINOR_OP_WRSR2, &sr2, 1);
	ret = nor->write_reg(nor, SPINOR_OP_WRSR2, sr2, 1);
	if (ret < 0) {
	if (ret < 0) {
		dev_err(nor->dev, "error while writing status register 2\n");
		dev_err(nor->dev, "error while writing status register 2\n");
		return -EINVAL;
		return -EINVAL;
@@ -1627,8 +1626,8 @@ static int sr2_bit7_quad_enable(struct spi_nor *nor)
	}
	}


	/* Read back and check it. */
	/* Read back and check it. */
	ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1);
	ret = nor->read_reg(nor, SPINOR_OP_RDSR2, sr2, 1);
	if (!(ret > 0 && (sr2 & SR2_QUAD_EN_BIT7))) {
	if (!(ret > 0 && (*sr2 & SR2_QUAD_EN_BIT7))) {
		dev_err(nor->dev, "SR2 Quad bit not set\n");
		dev_err(nor->dev, "SR2 Quad bit not set\n");
		return -EINVAL;
		return -EINVAL;
	}
	}
@@ -1687,7 +1686,7 @@ static int spi_nor_spansion_clear_sr_bp(struct spi_nor *nor)
{
{
	int ret;
	int ret;
	u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
	u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
	u8 sr_cr[2] = {0};
	u8 *sr_cr =  nor->bouncebuf;


	/* Check current Quad Enable bit value. */
	/* Check current Quad Enable bit value. */
	ret = read_cr(nor);
	ret = read_cr(nor);
@@ -2177,7 +2176,7 @@ static const struct flash_info spi_nor_ids[] = {
static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
{
{
	int			tmp;
	int			tmp;
	u8			id[SPI_NOR_MAX_ID_LEN];
	u8			*id = nor->bouncebuf;
	const struct flash_info	*info;
	const struct flash_info	*info;


	tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
	tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
@@ -2393,9 +2392,8 @@ static int spi_nor_check(struct spi_nor *nor)
static int s3an_nor_scan(struct spi_nor *nor)
static int s3an_nor_scan(struct spi_nor *nor)
{
{
	int ret;
	int ret;
	u8 val;


	ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1);
	ret = nor->read_reg(nor, SPINOR_OP_XRDSR, nor->bouncebuf, 1);
	if (ret < 0) {
	if (ret < 0) {
		dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);
		dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);
		return ret;
		return ret;
@@ -2417,7 +2415,7 @@ static int s3an_nor_scan(struct spi_nor *nor)
	 * The current addressing mode can be read from the XRDSR register
	 * The current addressing mode can be read from the XRDSR register
	 * and should not be changed, because is a destructive operation.
	 * and should not be changed, because is a destructive operation.
	 */
	 */
	if (val & XSR_PAGESIZE) {
	if (nor->bouncebuf[0] & XSR_PAGESIZE) {
		/* Flash in Power of 2 mode */
		/* Flash in Power of 2 mode */
		nor->page_size = (nor->page_size == 264) ? 256 : 512;
		nor->page_size = (nor->page_size == 264) ? 256 : 512;
		nor->mtd.writebufsize = nor->page_size;
		nor->mtd.writebufsize = nor->page_size;
@@ -4121,6 +4119,16 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
	nor->read_proto = SNOR_PROTO_1_1_1;
	nor->read_proto = SNOR_PROTO_1_1_1;
	nor->write_proto = SNOR_PROTO_1_1_1;
	nor->write_proto = SNOR_PROTO_1_1_1;


	/*
	 * We need the bounce buffer early to read/write registers when going
	 * through the spi-mem layer (buffers have to be DMA-able).
	 */
	nor->bouncebuf_size = PAGE_SIZE;
	nor->bouncebuf = devm_kmalloc(dev, nor->bouncebuf_size,
				      GFP_KERNEL);
	if (!nor->bouncebuf)
		return -ENOMEM;

	if (name)
	if (name)
		info = spi_nor_match_id(name);
		info = spi_nor_match_id(name);
	/* Try to auto-detect if chip name wasn't specified or not found */
	/* Try to auto-detect if chip name wasn't specified or not found */
+5 −2
Original line number Original line Diff line number Diff line
@@ -344,6 +344,9 @@ struct flash_info;
 * @mtd:		point to a mtd_info structure
 * @mtd:		point to a mtd_info structure
 * @lock:		the lock for the read/write/erase/lock/unlock operations
 * @lock:		the lock for the read/write/erase/lock/unlock operations
 * @dev:		point to a spi device, or a spi nor controller device.
 * @dev:		point to a spi device, or a spi nor controller device.
 * @bouncebuf:		bounce buffer used when the buffer passed by the MTD
 *                      layer is not DMA-able
 * @bouncebuf_size:	size of the bounce buffer
 * @info:		spi-nor part JDEC MFR id and other info
 * @info:		spi-nor part JDEC MFR id and other info
 * @page_size:		the page size of the SPI NOR
 * @page_size:		the page size of the SPI NOR
 * @addr_width:		number of address bytes
 * @addr_width:		number of address bytes
@@ -356,7 +359,6 @@ struct flash_info;
 * @read_proto:		the SPI protocol for read operations
 * @read_proto:		the SPI protocol for read operations
 * @write_proto:	the SPI protocol for write operations
 * @write_proto:	the SPI protocol for write operations
 * @reg_proto		the SPI protocol for read_reg/write_reg/erase operations
 * @reg_proto		the SPI protocol for read_reg/write_reg/erase operations
 * @cmd_buf:		used by the write_reg
 * @erase_map:		the erase map of the SPI NOR
 * @erase_map:		the erase map of the SPI NOR
 * @prepare:		[OPTIONAL] do some preparations for the
 * @prepare:		[OPTIONAL] do some preparations for the
 *			read/write/erase/lock/unlock operations
 *			read/write/erase/lock/unlock operations
@@ -382,6 +384,8 @@ struct spi_nor {
	struct mtd_info		mtd;
	struct mtd_info		mtd;
	struct mutex		lock;
	struct mutex		lock;
	struct device		*dev;
	struct device		*dev;
	u8			*bouncebuf;
	size_t			bouncebuf_size;
	const struct flash_info	*info;
	const struct flash_info	*info;
	u32			page_size;
	u32			page_size;
	u8			addr_width;
	u8			addr_width;
@@ -394,7 +398,6 @@ struct spi_nor {
	enum spi_nor_protocol	reg_proto;
	enum spi_nor_protocol	reg_proto;
	bool			sst_write_second;
	bool			sst_write_second;
	u32			flags;
	u32			flags;
	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
	struct spi_nor_erase_map	erase_map;
	struct spi_nor_erase_map	erase_map;


	int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
	int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);