summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
authorChin-Ting Kuo <chin-ting_kuo@aspeedtech.com>2022-08-19 12:01:09 +0300
committerTom Rini <trini@konsulko.com>2022-09-13 19:08:41 +0300
commit463cdf66632a0d67bf824cb43b6c1b41782d0765 (patch)
treebc03a523cf1768acdce08daaf827478687738fa2 /drivers/mtd
parentf7e1de4c6a43beec438bce04571469145086efed (diff)
downloadu-boot-463cdf66632a0d67bf824cb43b6c1b41782d0765.tar.xz
mtd: spi-nor: Use spi-mem dirmap API
This adds support for the dirmap API to the spi-nor subsystem, as introduced in Linux commit df5c21002cf4 ("mtd: spi-nor: use spi-mem dirmap API"). This patch is synchronize from the following patch https://patchwork.ozlabs.org/project/uboot/patch/20210205043924.149504-4-seanga2@gmail.com/ The corresponding Linux kernel SHA1 is df5c21002cf4. Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com> Signed-off-by: Sean Anderson <seanga2@gmail.com> Acked-by: Pratyush Yadav <p.yadav@ti.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/spi/sf_probe.c76
-rw-r--r--drivers/mtd/spi/spi-nor-core.c55
2 files changed, 115 insertions, 16 deletions
diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c
index f461082e03..e192f97efd 100644
--- a/drivers/mtd/spi/sf_probe.c
+++ b/drivers/mtd/spi/sf_probe.c
@@ -10,13 +10,69 @@
#include <common.h>
#include <dm.h>
#include <errno.h>
+#include <linux/mtd/spi-nor.h>
#include <log.h>
#include <malloc.h>
#include <spi.h>
#include <spi_flash.h>
+#include <spi-mem.h>
#include "sf_internal.h"
+static int spi_nor_create_read_dirmap(struct spi_nor *nor)
+{
+ struct spi_mem_dirmap_info info = {
+ .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0),
+ SPI_MEM_OP_ADDR(nor->addr_width, 0, 0),
+ SPI_MEM_OP_DUMMY(nor->read_dummy, 0),
+ SPI_MEM_OP_DATA_IN(0, NULL, 0)),
+ .offset = 0,
+ .length = nor->mtd.size,
+ };
+ struct spi_mem_op *op = &info.op_tmpl;
+
+ /* get transfer protocols. */
+ spi_nor_setup_op(nor, op, nor->read_proto);
+ op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
+
+ /* convert the dummy cycles to the number of bytes */
+ op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8;
+ if (spi_nor_protocol_is_dtr(nor->read_proto))
+ op->dummy.nbytes *= 2;
+
+ nor->dirmap.rdesc = spi_mem_dirmap_create(nor->spi, &info);
+ if (IS_ERR(nor->dirmap.rdesc))
+ return PTR_ERR(nor->dirmap.rdesc);
+
+ return 0;
+}
+
+static int spi_nor_create_write_dirmap(struct spi_nor *nor)
+{
+ struct spi_mem_dirmap_info info = {
+ .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0),
+ SPI_MEM_OP_ADDR(nor->addr_width, 0, 0),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(0, NULL, 0)),
+ .offset = 0,
+ .length = nor->mtd.size,
+ };
+ struct spi_mem_op *op = &info.op_tmpl;
+
+ /* get transfer protocols. */
+ spi_nor_setup_op(nor, op, nor->write_proto);
+ op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
+
+ if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
+ op->addr.nbytes = 0;
+
+ nor->dirmap.wdesc = spi_mem_dirmap_create(nor->spi, &info);
+ if (IS_ERR(nor->dirmap.wdesc))
+ return PTR_ERR(nor->dirmap.wdesc);
+
+ return 0;
+}
+
/**
* spi_flash_probe_slave() - Probe for a SPI flash device on a bus
*
@@ -45,6 +101,16 @@ static int spi_flash_probe_slave(struct spi_flash *flash)
if (ret)
goto err_read_id;
+ if (CONFIG_IS_ENABLED(SPI_DIRMAP)) {
+ ret = spi_nor_create_read_dirmap(flash);
+ if (ret)
+ return ret;
+
+ ret = spi_nor_create_write_dirmap(flash);
+ if (ret)
+ return ret;
+ }
+
if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
ret = spi_flash_mtd_register(flash);
@@ -83,6 +149,11 @@ struct spi_flash *spi_flash_probe(unsigned int busnum, unsigned int cs,
void spi_flash_free(struct spi_flash *flash)
{
+ if (CONFIG_IS_ENABLED(SPI_DIRMAP)) {
+ spi_mem_dirmap_destroy(flash->dirmap.wdesc);
+ spi_mem_dirmap_destroy(flash->dirmap.rdesc);
+ }
+
if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
spi_flash_mtd_unregister(flash);
@@ -153,6 +224,11 @@ static int spi_flash_std_remove(struct udevice *dev)
struct spi_flash *flash = dev_get_uclass_priv(dev);
int ret;
+ if (CONFIG_IS_ENABLED(SPI_DIRMAP)) {
+ spi_mem_dirmap_destroy(flash->dirmap.wdesc);
+ spi_mem_dirmap_destroy(flash->dirmap.rdesc);
+ }
+
ret = spi_nor_remove(flash);
if (ret)
return ret;
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index e3c86e080a..f236e87510 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -246,9 +246,9 @@ static u8 spi_nor_get_cmd_ext(const struct spi_nor *nor,
* need to be initialized.
* @proto: the protocol from which the properties need to be set.
*/
-static void spi_nor_setup_op(const struct spi_nor *nor,
- struct spi_mem_op *op,
- const enum spi_nor_protocol proto)
+void spi_nor_setup_op(const struct spi_nor *nor,
+ struct spi_mem_op *op,
+ const enum spi_nor_protocol proto)
{
u8 ext;
@@ -369,13 +369,29 @@ static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
while (remaining) {
op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
- ret = spi_mem_adjust_op_size(nor->spi, &op);
- if (ret)
- return ret;
- ret = spi_mem_exec_op(nor->spi, &op);
- if (ret)
- return ret;
+ if (CONFIG_IS_ENABLED(SPI_DIRMAP) && nor->dirmap.rdesc) {
+ /*
+ * Record current operation information which may be used
+ * when the address or data length exceeds address mapping.
+ */
+ memcpy(&nor->dirmap.rdesc->info.op_tmpl, &op,
+ sizeof(struct spi_mem_op));
+ ret = spi_mem_dirmap_read(nor->dirmap.rdesc,
+ op.addr.val, op.data.nbytes,
+ op.data.buf.in);
+ if (ret < 0)
+ return ret;
+ op.data.nbytes = ret;
+ } else {
+ ret = spi_mem_adjust_op_size(nor->spi, &op);
+ if (ret)
+ return ret;
+
+ ret = spi_mem_exec_op(nor->spi, &op);
+ if (ret)
+ return ret;
+ }
op.addr.val += op.data.nbytes;
remaining -= op.data.nbytes;
@@ -400,14 +416,21 @@ static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
spi_nor_setup_op(nor, &op, nor->write_proto);
- ret = spi_mem_adjust_op_size(nor->spi, &op);
- if (ret)
- return ret;
- op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes;
+ if (CONFIG_IS_ENABLED(SPI_DIRMAP) && nor->dirmap.wdesc) {
+ memcpy(&nor->dirmap.wdesc->info.op_tmpl, &op,
+ sizeof(struct spi_mem_op));
+ op.data.nbytes = spi_mem_dirmap_write(nor->dirmap.wdesc, op.addr.val,
+ op.data.nbytes, op.data.buf.out);
+ } else {
+ ret = spi_mem_adjust_op_size(nor->spi, &op);
+ if (ret)
+ return ret;
+ op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes;
- ret = spi_mem_exec_op(nor->spi, &op);
- if (ret)
- return ret;
+ ret = spi_mem_exec_op(nor->spi, &op);
+ if (ret)
+ return ret;
+ }
return op.data.nbytes;
}