327 lines
10 KiB
Diff
327 lines
10 KiB
Diff
From 32b2c87f0bc38bca34563d24d35086b5442413d9 Mon Sep 17 00:00:00 2001
|
|
From: Vignesh Raghavendra <vigneshr@ti.com>
|
|
Date: Tue, 6 Aug 2019 10:40:39 +0530
|
|
Subject: [PATCH 03/39] mtd: spi-nor: always use bounce buffer for register
|
|
read/writes
|
|
|
|
mainline inclusion
|
|
from mainline-v5.4-rc1
|
|
commit f173f26a4d543fa57e6ed9505bbbbf9bec61f544
|
|
category: feature
|
|
bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP
|
|
CVE: NA
|
|
|
|
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f173f26a4d543fa57e6ed9505bbbbf9bec61f544
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
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: Vignesh Raghavendra <vigneshr@ti.com>
|
|
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
|
|
Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com>
|
|
Signed-off-by: YunYi Yang <yangyunyi2@huawei.com>
|
|
|
|
Conflicts:
|
|
drivers/mtd/spi-nor/spi-nor.c
|
|
include/linux/mtd/spi-nor.h
|
|
---
|
|
drivers/mtd/spi-nor/spi-nor.c | 78 +++++++++++++++++++----------------
|
|
include/linux/mtd/spi-nor.h | 7 +++-
|
|
2 files changed, 48 insertions(+), 37 deletions(-)
|
|
|
|
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
|
|
index 5440e33b360c..7771d4519245 100644
|
|
--- a/drivers/mtd/spi-nor/spi-nor.c
|
|
+++ b/drivers/mtd/spi-nor/spi-nor.c
|
|
@@ -105,15 +105,14 @@ static const struct flash_info *spi_nor_match_id(const char *name);
|
|
static int read_sr(struct spi_nor *nor)
|
|
{
|
|
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) {
|
|
pr_err("error %d reading SR\n", (int) ret);
|
|
return ret;
|
|
}
|
|
|
|
- return val;
|
|
+ return nor->bouncebuf[0];
|
|
}
|
|
|
|
/*
|
|
@@ -124,15 +123,14 @@ static int read_sr(struct spi_nor *nor)
|
|
static int read_fsr(struct spi_nor *nor)
|
|
{
|
|
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) {
|
|
pr_err("error %d reading FSR\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
- return val;
|
|
+ return nor->bouncebuf[0];
|
|
}
|
|
|
|
/*
|
|
@@ -143,15 +141,14 @@ static int read_fsr(struct spi_nor *nor)
|
|
static int read_cr(struct spi_nor *nor)
|
|
{
|
|
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) {
|
|
dev_err(nor->dev, "error %d reading CR\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
- return val;
|
|
+ return nor->bouncebuf[0];
|
|
}
|
|
|
|
/*
|
|
@@ -160,8 +157,8 @@ static int read_cr(struct spi_nor *nor)
|
|
*/
|
|
static inline int write_sr(struct spi_nor *nor, u8 val)
|
|
{
|
|
- nor->cmd_buf[0] = val;
|
|
- return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1);
|
|
+ nor->bouncebuf[0] = val;
|
|
+ return nor->write_reg(nor, SPINOR_OP_WRSR, nor->bouncebuf, 1);
|
|
}
|
|
|
|
/*
|
|
@@ -293,31 +290,31 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
|
|
* We must clear the register to enable normal behavior.
|
|
*/
|
|
write_enable(nor);
|
|
- nor->cmd_buf[0] = 0;
|
|
- nor->write_reg(nor, SPINOR_OP_WREAR, nor->cmd_buf, 1);
|
|
+ nor->bouncebuf[0] = 0;
|
|
+ nor->write_reg(nor, SPINOR_OP_WREAR,
|
|
+ nor->bouncebuf, 1);
|
|
write_disable(nor);
|
|
}
|
|
|
|
return status;
|
|
default:
|
|
/* Spansion style */
|
|
- nor->cmd_buf[0] = enable << 7;
|
|
- return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
|
|
+ nor->bouncebuf[0] = enable << 7;
|
|
+ return nor->write_reg(nor, SPINOR_OP_BRWR, nor->bouncebuf, 1);
|
|
}
|
|
}
|
|
|
|
static int s3an_sr_ready(struct spi_nor *nor)
|
|
{
|
|
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) {
|
|
dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);
|
|
return ret;
|
|
}
|
|
|
|
- return !!(val & XSR_RDY);
|
|
+ return !!(nor->bouncebuf[0] & XSR_RDY);
|
|
}
|
|
|
|
static inline int spi_nor_sr_ready(struct spi_nor *nor)
|
|
@@ -476,7 +473,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)
|
|
{
|
|
- u8 buf[SPI_NOR_MAX_ADDR_WIDTH];
|
|
int i;
|
|
|
|
if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
|
|
@@ -490,11 +486,12 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
|
|
* control
|
|
*/
|
|
for (i = nor->addr_width - 1; i >= 0; i--) {
|
|
- buf[i] = addr & 0xff;
|
|
+ nor->bouncebuf[i] = addr & 0xff;
|
|
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);
|
|
}
|
|
|
|
/*
|
|
@@ -1271,7 +1268,7 @@ static const struct flash_info spi_nor_ids[] = {
|
|
static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
|
|
{
|
|
int tmp;
|
|
- u8 id[SPI_NOR_MAX_ID_LEN];
|
|
+ u8 *id = nor->bouncebuf;
|
|
const struct flash_info *info;
|
|
|
|
tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
|
|
@@ -1571,9 +1568,11 @@ static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr)
|
|
*/
|
|
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;
|
|
|
|
+ sr_cr[0] = 0;
|
|
+ sr_cr[1] = CR_QUAD_EN_SPAN;
|
|
ret = write_sr_cr(nor, sr_cr);
|
|
if (ret)
|
|
return ret;
|
|
@@ -1603,7 +1602,7 @@ static int spansion_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;
|
|
|
|
/* Keep the current value of the Status Register. */
|
|
@@ -1634,7 +1633,7 @@ static int spansion_no_read_cr_quad_enable(struct spi_nor *nor)
|
|
static int spansion_read_cr_quad_enable(struct spi_nor *nor)
|
|
{
|
|
struct device *dev = nor->dev;
|
|
- u8 sr_cr[2];
|
|
+ u8 *sr_cr = nor->bouncebuf;
|
|
int ret;
|
|
|
|
/* Check current Quad Enable bit value. */
|
|
@@ -1685,22 +1684,22 @@ static int spansion_read_cr_quad_enable(struct spi_nor *nor)
|
|
*/
|
|
static int sr2_bit7_quad_enable(struct spi_nor *nor)
|
|
{
|
|
- u8 sr2;
|
|
+ u8 *sr2 = nor->bouncebuf;
|
|
int ret;
|
|
|
|
/* 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)
|
|
return ret;
|
|
- if (sr2 & SR2_QUAD_EN_BIT7)
|
|
+ if (*sr2 & SR2_QUAD_EN_BIT7)
|
|
return 0;
|
|
|
|
/* Update the Quad Enable bit. */
|
|
- sr2 |= SR2_QUAD_EN_BIT7;
|
|
+ *sr2 |= SR2_QUAD_EN_BIT7;
|
|
|
|
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) {
|
|
dev_err(nor->dev, "error while writing status register 2\n");
|
|
return -EINVAL;
|
|
@@ -1713,8 +1712,8 @@ static int sr2_bit7_quad_enable(struct spi_nor *nor)
|
|
}
|
|
|
|
/* Read back and check it. */
|
|
- ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1);
|
|
- if (!(ret > 0 && (sr2 & SR2_QUAD_EN_BIT7))) {
|
|
+ ret = nor->read_reg(nor, SPINOR_OP_RDSR2, sr2, 1);
|
|
+ if (!(ret > 0 && (*sr2 & SR2_QUAD_EN_BIT7))) {
|
|
dev_err(nor->dev, "SR2 Quad bit not set\n");
|
|
return -EINVAL;
|
|
}
|
|
@@ -1736,9 +1735,8 @@ static int spi_nor_check(struct spi_nor *nor)
|
|
static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor)
|
|
{
|
|
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) {
|
|
dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);
|
|
return ret;
|
|
@@ -1760,7 +1758,7 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor)
|
|
* The current addressing mode can be read from the XRDSR register
|
|
* 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 */
|
|
nor->page_size = (nor->page_size == 264) ? 256 : 512;
|
|
nor->mtd.writebufsize = nor->page_size;
|
|
@@ -2819,6 +2817,16 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
|
|
nor->read_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)
|
|
info = spi_nor_match_id(name);
|
|
/* Try to auto-detect if chip name wasn't specified or not found */
|
|
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
|
|
index c922e97f205a..66431fd2a48b 100644
|
|
--- a/include/linux/mtd/spi-nor.h
|
|
+++ b/include/linux/mtd/spi-nor.h
|
|
@@ -249,6 +249,9 @@ struct flash_info;
|
|
* @mtd: point to a mtd_info structure
|
|
* @lock: the lock for the read/write/erase/lock/unlock operations
|
|
* @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
|
|
* @page_size: the page size of the SPI NOR
|
|
* @addr_width: number of address bytes
|
|
@@ -261,7 +264,6 @@ struct flash_info;
|
|
* @read_proto: the SPI protocol for read operations
|
|
* @write_proto: the SPI protocol for write operations
|
|
* @reg_proto the SPI protocol for read_reg/write_reg/erase operations
|
|
- * @cmd_buf: used by the write_reg
|
|
* @prepare: [OPTIONAL] do some preparations for the
|
|
* read/write/erase/lock/unlock operations
|
|
* @unprepare: [OPTIONAL] do some post work after the
|
|
@@ -284,6 +286,8 @@ struct spi_nor {
|
|
struct mtd_info mtd;
|
|
struct mutex lock;
|
|
struct device *dev;
|
|
+ u8 *bouncebuf;
|
|
+ size_t bouncebuf_size;
|
|
const struct flash_info *info;
|
|
u32 page_size;
|
|
u8 addr_width;
|
|
@@ -296,7 +300,6 @@ struct spi_nor {
|
|
enum spi_nor_protocol reg_proto;
|
|
bool sst_write_second;
|
|
u32 flags;
|
|
- u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
|
|
|
|
int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
|
|
void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
|
|
--
|
|
2.27.0
|
|
|