diff --git a/drivers/spi/ti_qspi.c b/drivers/spi/ti_qspi.c
index 731fb23022d2..77fa17ee8ab1 100644
--- a/drivers/spi/ti_qspi.c
+++ b/drivers/spi/ti_qspi.c
@@ -10,6 +10,7 @@
 #include <asm/arch/omap.h>
 #include <malloc.h>
 #include <spi.h>
+#include <spi-mem.h>
 #include <dm.h>
 #include <asm/gpio.h>
 #include <asm/omap_gpio.h>
@@ -40,7 +41,6 @@ DECLARE_GLOBAL_DATA_PTR;
 #define QSPI_INVAL                      (4 << 16)
 #define QSPI_RD_QUAD                    (7 << 16)
 /* device control */
-#define QSPI_DD(m, n)                   (m << (3 + n*8))
 #define QSPI_CKPHA(n)                   (1 << (2 + n*8))
 #define QSPI_CSPOL(n)                   (1 << (1 + n*8))
 #define QSPI_CKPOL(n)                   (1 << (n*8))
@@ -53,18 +53,11 @@ DECLARE_GLOBAL_DATA_PTR;
 #define MEM_CS(cs)                      ((cs + 1) << 8)
 #define MEM_CS_UNSELECT                 0xfffff8ff
 
-#define QSPI_CMD_READ                   (0x3 << 0)
-#define QSPI_CMD_READ_DUAL		(0x6b << 0)
-#define QSPI_CMD_READ_QUAD              (0x6c << 0)
-#define QSPI_CMD_READ_FAST              (0x0b << 0)
-#define QSPI_SETUP0_NUM_A_BYTES         (0x3 << 8)
-#define QSPI_SETUP0_NUM_D_BYTES_NO_BITS (0x0 << 10)
-#define QSPI_SETUP0_NUM_D_BYTES_8_BITS  (0x1 << 10)
 #define QSPI_SETUP0_READ_NORMAL         (0x0 << 12)
 #define QSPI_SETUP0_READ_DUAL           (0x1 << 12)
 #define QSPI_SETUP0_READ_QUAD           (0x3 << 12)
-#define QSPI_CMD_WRITE                  (0x12 << 16)
-#define QSPI_NUM_DUMMY_BITS             (0x0 << 24)
+#define QSPI_SETUP0_ADDR_SHIFT		(8)
+#define QSPI_SETUP0_DBITS_SHIFT		(10)
 
 /* ti qspi register set */
 struct ti_qspi_regs {
@@ -96,6 +89,7 @@ struct ti_qspi_regs {
 /* ti qspi priv */
 struct ti_qspi_priv {
 	void *memory_map;
+	size_t mmap_size;
 	uint max_hz;
 	u32 num_cs;
 	struct ti_qspi_regs *base;
@@ -171,19 +165,6 @@ static int ti_qspi_xfer(struct udevice *dev, unsigned int bitlen,
 		return -EINVAL;
 	}
 
-	/* Setup mmap flags */
-	if (flags & SPI_XFER_MMAP) {
-		writel(MM_SWITCH, &priv->base->memswitch);
-		if (priv->ctrl_mod_mmap)
-			ti_qspi_ctrl_mode_mmap(priv->ctrl_mod_mmap, cs, true);
-		return 0;
-	} else if (flags & SPI_XFER_MMAP_END) {
-		writel(~MM_SWITCH, &priv->base->memswitch);
-		if (priv->ctrl_mod_mmap)
-			ti_qspi_ctrl_mode_mmap(priv->ctrl_mod_mmap, cs, false);
-		return 0;
-	}
-
 	if (bitlen == 0)
 		return -1;
 
@@ -269,9 +250,9 @@ static int ti_qspi_xfer(struct udevice *dev, unsigned int bitlen,
 }
 
 /* TODO: control from sf layer to here through dm-spi */
-#if defined(CONFIG_TI_EDMA3) && !defined(CONFIG_DMA)
-void spi_flash_copy_mmap(void *data, void *offset, size_t len)
+static void ti_qspi_copy_mmap(void *data, void *offset, size_t len)
 {
+#if defined(CONFIG_TI_EDMA3) && !defined(CONFIG_DMA)
 	unsigned int			addr = (unsigned int) (data);
 	unsigned int			edma_slot_num = 1;
 
@@ -286,44 +267,34 @@ void spi_flash_copy_mmap(void *data, void *offset, size_t len)
 
 	/* disable edma3 clocks */
 	disable_edma3_clocks();
+#else
+	memcpy_fromio(data, offset, len);
+#endif
 
 	*((unsigned int *)offset) += len;
 }
-#endif
 
-static void __ti_qspi_setup_memorymap(struct ti_qspi_priv *priv,
-				      struct spi_slave *slave,
-				      bool enable)
+static void ti_qspi_setup_mmap_read(struct ti_qspi_priv *priv, u8 opcode,
+				    u8 data_nbits, u8 addr_width,
+				    u8 dummy_bytes)
 {
-	u32 memval;
-	u32 mode = slave->mode & (SPI_RX_QUAD | SPI_RX_DUAL);
+	u32 memval = opcode;
 
-	if (!enable) {
-		writel(0, &priv->base->setup0);
-		return;
-	}
-
-	memval = QSPI_SETUP0_NUM_A_BYTES | QSPI_CMD_WRITE | QSPI_NUM_DUMMY_BITS;
-
-	switch (mode) {
-	case SPI_RX_QUAD:
-		memval |= QSPI_CMD_READ_QUAD;
-		memval |= QSPI_SETUP0_NUM_D_BYTES_8_BITS;
+	switch (data_nbits) {
+	case 4:
 		memval |= QSPI_SETUP0_READ_QUAD;
-		slave->mode |= SPI_RX_QUAD;
 		break;
-	case SPI_RX_DUAL:
-		memval |= QSPI_CMD_READ_DUAL;
-		memval |= QSPI_SETUP0_NUM_D_BYTES_8_BITS;
+	case 2:
 		memval |= QSPI_SETUP0_READ_DUAL;
 		break;
 	default:
-		memval |= QSPI_CMD_READ;
-		memval |= QSPI_SETUP0_NUM_D_BYTES_NO_BITS;
 		memval |= QSPI_SETUP0_READ_NORMAL;
 		break;
 	}
 
+	memval |= ((addr_width - 1) << QSPI_SETUP0_ADDR_SHIFT |
+		   dummy_bytes << QSPI_SETUP0_DBITS_SHIFT);
+
 	writel(memval, &priv->base->setup0);
 }
 
@@ -342,10 +313,39 @@ static int ti_qspi_set_mode(struct udevice *bus, uint mode)
 	return 0;
 }
 
+static int ti_qspi_exec_mem_op(struct spi_slave *slave,
+			       const struct spi_mem_op *op)
+{
+	struct ti_qspi_priv *priv;
+	struct udevice *bus;
+
+	bus = slave->dev->parent;
+	priv = dev_get_priv(bus);
+	u32 from = 0;
+	int ret = 0;
+
+	/* Only optimize read path. */
+	if (!op->data.nbytes || op->data.dir != SPI_MEM_DATA_IN ||
+	    !op->addr.nbytes || op->addr.nbytes > 4)
+		return -ENOTSUPP;
+
+	/* Address exceeds MMIO window size, fall back to regular mode. */
+	from = op->addr.val;
+	if (from + op->data.nbytes > priv->mmap_size)
+		return -ENOTSUPP;
+
+	ti_qspi_setup_mmap_read(priv, op->cmd.opcode, op->data.buswidth,
+				op->addr.nbytes, op->dummy.nbytes);
+
+	ti_qspi_copy_mmap((void *)op->data.buf.in,
+			  (void *)priv->memory_map + from, op->data.nbytes);
+
+	return ret;
+}
+
 static int ti_qspi_claim_bus(struct udevice *dev)
 {
 	struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
-	struct spi_slave *slave = dev_get_parent_priv(dev);
 	struct ti_qspi_priv *priv;
 	struct udevice *bus;
 
@@ -357,7 +357,10 @@ static int ti_qspi_claim_bus(struct udevice *dev)
 		return -EINVAL;
 	}
 
-	__ti_qspi_setup_memorymap(priv, slave, true);
+	writel(MM_SWITCH, &priv->base->memswitch);
+	if (priv->ctrl_mod_mmap)
+		ti_qspi_ctrl_mode_mmap(priv->ctrl_mod_mmap,
+				       slave_plat->cs, true);
 
 	writel(priv->dc, &priv->base->dc);
 	writel(0, &priv->base->cmd);
@@ -371,18 +374,22 @@ static int ti_qspi_claim_bus(struct udevice *dev)
 
 static int ti_qspi_release_bus(struct udevice *dev)
 {
-	struct spi_slave *slave = dev_get_parent_priv(dev);
+	struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
 	struct ti_qspi_priv *priv;
 	struct udevice *bus;
 
 	bus = dev->parent;
 	priv = dev_get_priv(bus);
 
-	__ti_qspi_setup_memorymap(priv, slave, false);
+	writel(~MM_SWITCH, &priv->base->memswitch);
+	if (priv->ctrl_mod_mmap)
+		ti_qspi_ctrl_mode_mmap(priv->ctrl_mod_mmap,
+				       slave_plat->cs, false);
 
 	writel(0, &priv->base->dc);
 	writel(0, &priv->base->cmd);
 	writel(0, &priv->base->data);
+	writel(0, &priv->base->setup0);
 
 	return 0;
 }
@@ -440,12 +447,15 @@ static int ti_qspi_ofdata_to_platdata(struct udevice *bus)
 	struct ti_qspi_priv *priv = dev_get_priv(bus);
 	const void *blob = gd->fdt_blob;
 	int node = dev_of_offset(bus);
+	fdt_addr_t mmap_addr;
+	fdt_addr_t mmap_size;
 
 	priv->ctrl_mod_mmap = map_syscon_chipselects(bus);
 	priv->base = map_physmem(devfdt_get_addr(bus),
 				 sizeof(struct ti_qspi_regs), MAP_NOCACHE);
-	priv->memory_map = map_physmem(devfdt_get_addr_index(bus, 1), 0,
-				       MAP_NOCACHE);
+	mmap_addr = devfdt_get_addr_size_index(bus, 1, &mmap_size);
+	priv->memory_map = map_physmem(mmap_addr, mmap_size, MAP_NOCACHE);
+	priv->mmap_size = mmap_size;
 
 	priv->max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", -1);
 	if (priv->max_hz < 0) {
@@ -460,15 +470,9 @@ static int ti_qspi_ofdata_to_platdata(struct udevice *bus)
 	return 0;
 }
 
-static int ti_qspi_child_pre_probe(struct udevice *dev)
-{
-	struct spi_slave *slave = dev_get_parent_priv(dev);
-	struct udevice *bus = dev_get_parent(dev);
-	struct ti_qspi_priv *priv = dev_get_priv(bus);
-
-	slave->memory_map = priv->memory_map;
-	return 0;
-}
+static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
+	.exec_op = ti_qspi_exec_mem_op,
+};
 
 static const struct dm_spi_ops ti_qspi_ops = {
 	.claim_bus	= ti_qspi_claim_bus,
@@ -476,6 +480,7 @@ static const struct dm_spi_ops ti_qspi_ops = {
 	.xfer		= ti_qspi_xfer,
 	.set_speed	= ti_qspi_set_speed,
 	.set_mode	= ti_qspi_set_mode,
+	.mem_ops        = &ti_qspi_mem_ops,
 };
 
 static const struct udevice_id ti_qspi_ids[] = {
@@ -492,5 +497,4 @@ U_BOOT_DRIVER(ti_qspi) = {
 	.ofdata_to_platdata = ti_qspi_ofdata_to_platdata,
 	.priv_auto_alloc_size = sizeof(struct ti_qspi_priv),
 	.probe	= ti_qspi_probe,
-	.child_pre_probe = ti_qspi_child_pre_probe,
 };
