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>
diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c
index f461082..e192f97 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 @@
 	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 @@
 
 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 @@
 	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;