diff --git a/doc/device-tree-bindings/spi/spi-bus.txt b/doc/device-tree-bindings/spi/spi-bus.txt
index e57897ac0c..654d388cac 100644
--- a/doc/device-tree-bindings/spi/spi-bus.txt
+++ b/doc/device-tree-bindings/spi/spi-bus.txt
@@ -61,6 +61,10 @@ contain the following properties.
                       used for MISO. Defaults to 1 if not present.
 - spi-half-duplex  - (optional) Indicates that the SPI bus should wait for
 		      a header byte before reading data from the slave.
+- calibration-offset - (optional) Check pattern offset location for high-
+		        speed calibration.
+- calibration-length - (optional) Check pattern length for high-speed
+		        calibration.
 
 Some SPI controllers and devices support Dual and Quad SPI transfer mode.
 It allows data in SPI system transferred in 2 wires(DUAL) or 4 wires(QUAD).
diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig
index d068b7860e..ed0335d9ba 100644
--- a/drivers/mtd/spi/Kconfig
+++ b/drivers/mtd/spi/Kconfig
@@ -127,6 +127,18 @@ config SPI_FLASH_SOFT_RESET_ON_BOOT
 	 that are not supposed to be handed to U-Boot in Octal DTR mode, even
 	 if they _do_ support the Soft Reset sequence.
 
+config SPI_FLASH_HS_CALIB
+	bool "Support for high-speed SPI flash calibration"
+	default n
+	help
+	 Modern flash chips and controllers that operate at speeds higher than
+	 50MHz require delays in the signal lines to be calibrated. This
+	 feature adds a spi-mem API hook to allow controller drivers to
+	 perform the calibration during chip probe.
+	 Select N here if SPI flash chips or controllers on your platform
+	 operate roughly around or less than 50MHz, or if the controller driver
+	 doesn't implement the calibration hook.
+
 config SPI_FLASH_BAR
 	bool "SPI flash Bank/Extended address register support"
 	help
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index f86003ca8c..f164c3cf73 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -3230,6 +3230,152 @@ static int spi_nor_select_erase(struct spi_nor *nor,
 	return 0;
 }
 
+#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB)
+/**
+ * Very basic check to make sure every bit changes in the reference pattern
+ * and that there aren't too many duplicate bytes in a row, such as all FF's
+ * or all zeros.
+ * Returns boolean.
+ */
+static int spi_nor_spimem_validate_calib_data(u8 *buff, size_t len)
+{
+	size_t i;
+	u8 prev;
+	u8 changed_bits = 0;
+	size_t changing = 0;
+
+	if (!buff || !len)
+		return 0;
+
+	prev = *buff;
+	for (i = 1; i < len; ++i) {
+		u8 changed = prev ^ buff[i];
+
+		changed_bits |= changed;
+		if (changed)
+			++changing;
+		prev = buff[i];
+	}
+	return (changing > (len / 2)) && (changed_bits == 0xff);
+}
+
+/** Returns 0 on success, negative error code on error.
+ */
+static int spi_nor_spimem_calib_read_chk(struct spi_slave *slave)
+{
+	struct spi_nor *nor = dev_get_uclass_priv(slave->dev);
+
+	ssize_t readval = nor->read(nor, nor->calib_off,
+				    nor->calib_size,
+				    nor->calib_buff);
+	if (readval != nor->calib_size) {
+		if (readval >= 0)
+			return -EIO;
+		return readval;
+	}
+	return (memcmp(nor->calib_ref_buff,
+		       nor->calib_buff, nor->calib_size) != 0);
+}
+
+static int spi_nor_spimem_calib(struct spi_nor *nor)
+{
+	int retval = 0;
+
+	if (!nor->calib_buff || !nor->calib_ref_buff)
+		return retval;
+
+	retval = spi_mem_calibrate(nor->spi, spi_nor_spimem_calib_read_chk);
+
+	devm_kfree(nor->dev, nor->calib_buff);
+	nor->calib_buff = NULL;
+	devm_kfree(nor->dev, nor->calib_ref_buff);
+	nor->calib_ref_buff = NULL;
+
+	return retval;
+}
+
+static
+int spi_nor_spimem_calib_init(struct spi_nor *nor,
+			      const struct spi_nor_flash_parameter *params,
+			      u32 shared_caps_mask)
+{
+	int err;
+	ssize_t readval;
+	ofnode np = dev_ofnode(nor->spi->dev);
+	u8 old_addr_width = nor->addr_width;
+
+	if (!spi_mem_has_calibrate(nor->spi))
+		return 0;
+
+	/* Select a supported 1x read command to obtain calibration
+	 * reference data.
+	 */
+	shared_caps_mask &= (SNOR_HWCAPS_READ | SNOR_HWCAPS_READ_FAST);
+	err = spi_nor_select_read(nor, params, shared_caps_mask);
+	if (err) {
+		dev_warn(nor->dev,
+			 "Skipping SPI-nor calibration: Can't select 1x read settings supported by both the controller and memory.\n");
+		return 0;
+	}
+
+	/* Default to read the first 2 pages of the flash. Usually, this
+	 * would contain a bootloader image. While this isn't an ideal
+	 * calibration pattern, it's hopefully good enough to get something
+	 * working most of the time.
+	 */
+	ofnode_read_u32(np, "calibration-offset", &nor->calib_off);
+	ofnode_read_u32(np, "calibration-length", &nor->calib_size);
+	if (!nor->calib_size)
+		nor->calib_size = 2 * nor->page_size;
+
+	if (nor->addr_width == 4 ||
+	    ((nor->calib_off + nor->calib_size) > SZ_16M)) {
+		/* Only support a >16MB calibration address if the flash chip
+		 * supports 4 byte commands. Otherwise, it would require
+		 * enabling 4 byte mode. This isn't handled until later, in
+		 * spi_nor_init()
+		 */
+		if (!(nor->flags & SPI_NOR_4B_OPCODES)) {
+			dev_warn(nor->dev,
+				 "Skipping SPI-nor calibration: calibration currently only supports 3 byte addressing.\n");
+			return 0;
+		}
+		nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
+		nor->addr_width = 4;
+	} else {
+		nor->addr_width = 3;
+	}
+
+	nor->calib_ref_buff = devm_kmalloc(nor->dev, nor->calib_size,
+					   GFP_KERNEL);
+	nor->calib_buff = devm_kmalloc(nor->dev, nor->calib_size, GFP_KERNEL);
+	if (!nor->calib_ref_buff || !nor->calib_buff)
+		return -ENOMEM;
+
+	readval = nor->read(nor, nor->calib_off,
+			    nor->calib_size, nor->calib_ref_buff);
+	if (readval < 0)
+		return readval;
+	else if (nor->calib_size != readval)
+		return -EIO;
+
+	if (!spi_nor_spimem_validate_calib_data(nor->calib_ref_buff,
+						nor->calib_size)) {
+		devm_kfree(nor->dev, nor->calib_ref_buff);
+		devm_kfree(nor->dev, nor->calib_buff);
+		nor->calib_ref_buff = NULL;
+		nor->calib_buff = NULL;
+		dev_warn(nor->dev,
+			 "Skipping SPI-nor calibration: Calibration data has poor entropy.\n");
+	}
+
+	/* Restore settings. */
+	nor->addr_width = old_addr_width;
+
+	return 0;
+}
+#endif
+
 static int spi_nor_default_setup(struct spi_nor *nor,
 				 const struct flash_info *info,
 				 const struct spi_nor_flash_parameter *params)
@@ -3240,6 +3386,12 @@ static int spi_nor_default_setup(struct spi_nor *nor,
 
 	spi_nor_adjust_hwcaps(nor, params, &shared_mask);
 
+#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB)
+	err = spi_nor_spimem_calib_init(nor, params, shared_mask);
+	if (err)
+		return err;
+#endif
+
 	/* Select the (Fast) Read command. */
 	err = spi_nor_select_read(nor, params, shared_mask);
 	if (err) {
@@ -3892,6 +4044,14 @@ static int spi_nor_init(struct spi_nor *nor)
 		set_4byte(nor, nor->info, 1);
 	}
 
+#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB)
+	err = spi_nor_spimem_calib(nor);
+	if (err) {
+		dev_err(nor->dev, "SPI calibration failed\n");
+		return err;
+	}
+#endif
+
 	return 0;
 }
 
@@ -4057,6 +4217,14 @@ int spi_nor_scan(struct spi_nor *nor)
 	spi_nor_soft_reset(nor);
 #endif /* CONFIG_SPI_FLASH_SOFT_RESET_ON_BOOT */
 
+#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB)
+	if (spi_mem_has_calibrate(nor->spi)) {
+		ret = spi_mem_calibrate(nor->spi, NULL);
+		if (ret)
+			return ret;
+	}
+#endif
+
 	info = spi_nor_read_id(nor);
 	if (IS_ERR_OR_NULL(info))
 		return -ENOENT;
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index b7eca58359..03ede22216 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -763,6 +763,30 @@ ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
 }
 EXPORT_SYMBOL_GPL(spi_mem_dirmap_write);
 
+#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB)
+int spi_mem_has_calibrate(struct spi_slave *slave)
+{
+	struct udevice *bus = slave->dev->parent;
+	struct dm_spi_ops *ops = spi_get_ops(bus);
+
+	return (ops->mem_ops && ops->mem_ops->calibrate);
+}
+EXPORT_SYMBOL_GPL(spi_mem_has_calibrate);
+
+int spi_mem_calibrate(struct spi_slave *slave,
+		      int (*calib_chk_fn)(struct spi_slave *))
+{
+	struct udevice *bus = slave->dev->parent;
+	struct dm_spi_ops *ops = spi_get_ops(bus);
+
+	if (spi_mem_has_calibrate(slave))
+		return ops->mem_ops->calibrate(slave, calib_chk_fn);
+
+	return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(spi_mem_calibrate);
+#endif
+
 #ifndef __UBOOT__
 static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
 {
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index d1dbf3eadb..2eddb52392 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -593,6 +593,13 @@ struct spi_nor {
 	u32 size;
 	u32 sector_size;
 	u32 erase_size;
+
+#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB)
+	u8	*calib_ref_buff;
+	u8	*calib_buff;
+	u32	calib_size;
+	u32	calib_off;
+#endif
 };
 
 #ifndef __UBOOT__
diff --git a/include/spi-mem.h b/include/spi-mem.h
index 3c8e95b6f5..3db0f7e11c 100644
--- a/include/spi-mem.h
+++ b/include/spi-mem.h
@@ -243,6 +243,15 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
  *		  the currently mapped area), and the caller of
  *		  spi_mem_dirmap_write() is responsible for calling it again in
  *		  this case.
+ * @calibrate: instruct driver to perform or clear high speed calibration for
+ *	       the memory device. The provided function should be used by the
+ *	       driver to check if a calibration configuration is valid. May be
+ *	       called multiple times as needed. Do note that calling the check
+ *	       function may call back into the other ops in this interface.
+ *	       A null calib_chk_fn should clear any existing calibration.
+ *	       calib_chk_fn is expected to return 0 on success, negative error
+ *	       codes, and a positive value on failure.
+ *	       The same return value expectation for the calibrate function.
  *
  * This interface should be implemented by SPI controllers providing an
  * high-level interface to execute SPI memory operation, which is usually the
@@ -266,6 +275,10 @@ struct spi_controller_mem_ops {
 			       u64 offs, size_t len, void *buf);
 	ssize_t (*dirmap_write)(struct spi_mem_dirmap_desc *desc,
 				u64 offs, size_t len, const void *buf);
+#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB)
+	int (*calibrate)(struct spi_slave *slave,
+			 int (*calib_chk_fn)(struct spi_slave *));
+#endif
 };
 
 #ifndef __UBOOT__
@@ -341,6 +354,12 @@ ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
 ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
 			     u64 offs, size_t len, const void *buf);
 
+#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB)
+int spi_mem_has_calibrate(struct spi_slave *slave);
+int spi_mem_calibrate(struct spi_slave *slave,
+		      int (*calib_chk_fn)(struct spi_slave *));
+#endif
+
 #ifndef __UBOOT__
 int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
 				       struct module *owner);
