Merge branch 'master' of git://git.denx.de/u-boot-spi

- Various MTD fixes from Boris
- Zap various unused / legacy paths.
- pxa3xx NAND update from Miquel

Signed-off-by: Tom Rini <trini@konsulko.com>
diff --git a/README b/README
index a46c7c6..17d56b8 100644
--- a/README
+++ b/README
@@ -1932,14 +1932,6 @@
 		SPI configuration items (port pins to use, etc). For
 		an example, see include/configs/sacsng.h.
 
-		CONFIG_HARD_SPI
-
-		Enables a hardware SPI driver for general-purpose reads
-		and writes.  As with CONFIG_SOFT_SPI, the board configuration
-		must define a list of chip-select function pointers.
-		Currently supported on some MPC8xxx processors.	 For an
-		example, see include/configs/mpc8349emds.h.
-
 		CONFIG_SYS_SPI_MXC_WAIT
 		Timeout for waiting until spi transfer completed.
 		default: (CONFIG_SYS_HZ/100)     /* 10 ms */
diff --git a/arch/powerpc/include/asm/config.h b/arch/powerpc/include/asm/config.h
index 849a69a..c9c9964 100644
--- a/arch/powerpc/include/asm/config.h
+++ b/arch/powerpc/include/asm/config.h
@@ -18,13 +18,6 @@
   #define HWCONFIG_BUFFER_SIZE 256
 #endif
 
-/* CONFIG_HARD_SPI triggers SPI bus initialization in PowerPC */
-#if defined(CONFIG_MPC8XXX_SPI) || defined(CONFIG_FSL_ESPI)
-# ifndef CONFIG_HARD_SPI
-#  define CONFIG_HARD_SPI
-# endif
-#endif
-
 #define CONFIG_LMB
 #define CONFIG_SYS_BOOT_RAMDISK_HIGH
 
diff --git a/board/freescale/mpc8349emds/mpc8349emds.c b/board/freescale/mpc8349emds/mpc8349emds.c
index 4ec0af4..d40ed37 100644
--- a/board/freescale/mpc8349emds/mpc8349emds.c
+++ b/board/freescale/mpc8349emds/mpc8349emds.c
@@ -273,7 +273,7 @@
 
 	iopd->dat |=  SPI_CS_MASK;
 }
-#endif /* CONFIG_HARD_SPI */
+#endif
 
 #if defined(CONFIG_OF_BOARD_SETUP)
 int ft_board_setup(void *blob, bd_t *bd)
diff --git a/board/ids/ids8313/ids8313.c b/board/ids/ids8313/ids8313.c
index a411d4e..d547af4 100644
--- a/board/ids/ids8313/ids8313.c
+++ b/board/ids/ids8313/ids8313.c
@@ -208,4 +208,4 @@
 	/* deactivate the spi_cs */
 	setbits_be32(&iopd->dat, IDSCPLD_SPI_CS_MASK);
 }
-#endif /* CONFIG_HARD_SPI */
+#endif
diff --git a/cmd/eeprom.c b/cmd/eeprom.c
index e88cb13..6c29b33 100644
--- a/cmd/eeprom.c
+++ b/cmd/eeprom.c
@@ -66,11 +66,6 @@
 
 void eeprom_init(int bus)
 {
-	/* SPI EEPROM */
-#if defined(CONFIG_MPC8XX_SPI) && !defined(CONFIG_ENV_EEPROM_IS_ON_I2C)
-	spi_init_f();
-#endif
-
 	/* I2C EEPROM */
 #if defined(CONFIG_SYS_I2C)
 	if (bus >= 0)
@@ -129,14 +124,6 @@
 {
 	int ret = 0;
 
-	/* SPI */
-#if defined(CONFIG_MPC8XX_SPI) && !defined(CONFIG_ENV_EEPROM_IS_ON_I2C)
-	if (read)
-		spi_read(addr, alen, buffer, len);
-	else
-		spi_write(addr, alen, buffer, len);
-#else	/* I2C */
-
 #if defined(CONFIG_DM_I2C) && defined(CONFIG_SYS_I2C_EEPROM_BUS)
 	struct udevice *dev;
 
@@ -162,7 +149,6 @@
 		ret = i2c_read(addr[0], offset, alen - 1, buffer, len);
 	else
 		ret = i2c_write(addr[0], offset, alen - 1, buffer, len);
-#endif
 #endif /* CONFIG_DM_I2C && CONFIG_SYS_I2C_EEPROM_BUS */
 	if (ret)
 		ret = CMD_RET_FAILURE;
diff --git a/cmd/ubi.c b/cmd/ubi.c
index 2b74a98..a12ac70 100644
--- a/cmd/ubi.c
+++ b/cmd/ubi.c
@@ -101,7 +101,6 @@
 	return 1;
 }
 
-
 static int verify_mkvol_req(const struct ubi_device *ubi,
 			    const struct ubi_mkvol_req *req)
 {
@@ -415,7 +414,7 @@
 	return 0;
 }
 
-int ubi_detach(void)
+static int ubi_detach(void)
 {
 #ifdef CONFIG_CMD_UBIFS
 	/*
@@ -473,7 +472,6 @@
 	if (argc < 2)
 		return CMD_RET_USAGE;
 
-
 	if (strcmp(argv[1], "detach") == 0) {
 		if (argc < 2)
 			return CMD_RET_USAGE;
@@ -481,7 +479,6 @@
 		return ubi_detach();
 	}
 
-
 	if (strcmp(argv[1], "part") == 0) {
 		const char *vid_header_offset = NULL;
 
diff --git a/common/board_f.c b/common/board_f.c
index 835b724..149a722 100644
--- a/common/board_f.c
+++ b/common/board_f.c
@@ -24,7 +24,6 @@
 #include <os.h>
 #include <post.h>
 #include <relocate.h>
-#include <spi.h>
 #ifdef CONFIG_SPL
 #include <spl.h>
 #endif
@@ -262,16 +261,6 @@
 }
 #endif
 
-#if defined(CONFIG_HARD_SPI)
-static int init_func_spi(void)
-{
-	puts("SPI:   ");
-	spi_init();
-	puts("ready\n");
-	return 0;
-}
-#endif
-
 static int setup_mon_len(void)
 {
 #if defined(__ARM__) || defined(__MICROBLAZE__)
@@ -913,9 +902,6 @@
 #if defined(CONFIG_VID) && !defined(CONFIG_SPL)
 	init_func_vid,
 #endif
-#if defined(CONFIG_HARD_SPI)
-	init_func_spi,
-#endif
 	announce_dram_init,
 	dram_init,		/* configure available RAM banks */
 #ifdef CONFIG_POST
diff --git a/common/board_r.c b/common/board_r.c
index 21d3b3c..5f3d27a 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -36,7 +36,6 @@
 #include <onenand_uboot.h>
 #include <scsi.h>
 #include <serial.h>
-#include <spi.h>
 #include <stdio_dev.h>
 #include <timer.h>
 #include <trace.h>
@@ -379,20 +378,6 @@
 }
 #endif
 
-#if defined(CONFIG_PPC) && !defined(CONFIG_DM_SPI)
-static int initr_spi(void)
-{
-	/* MPC8xx does this here */
-#ifdef CONFIG_MPC8XX_SPI
-#if !defined(CONFIG_ENV_IS_IN_EEPROM)
-	spi_init_f();
-#endif
-	spi_init_r();
-#endif
-	return 0;
-}
-#endif
-
 #ifdef CONFIG_CMD_NAND
 /* go init the NAND */
 static int initr_nand(void)
@@ -744,9 +729,6 @@
 	/* initialize higher level parts of CPU like time base and timers */
 	cpu_init_r,
 #endif
-#if defined(CONFIG_PPC) && !defined(CONFIG_DM_SPI)
-	initr_spi,
-#endif
 #ifdef CONFIG_CMD_NAND
 	initr_nand,
 #endif
diff --git a/doc/driver-model/spi-howto.txt b/doc/driver-model/spi-howto.txt
index 1955ffe..38c26f6 100644
--- a/doc/driver-model/spi-howto.txt
+++ b/doc/driver-model/spi-howto.txt
@@ -163,11 +163,6 @@
 empty SPI driver. You still have empty methods in your driver, but we will
 write these one by one.
 
-If you have spi_init() functions or the like that are called from your
-board then the build will fail. Remove these calls and make a note of the
-init that needs to be done.
-
-
 7. Set up your platform data structure
 
 This will hold the information your driver to operate, like its hardware
diff --git a/drivers/mtd/mtd_uboot.c b/drivers/mtd/mtd_uboot.c
index 5ca560c..d638f70 100644
--- a/drivers/mtd/mtd_uboot.c
+++ b/drivers/mtd/mtd_uboot.c
@@ -13,6 +13,29 @@
 
 #define MTD_NAME_MAX_LEN 20
 
+void board_mtdparts_default(const char **mtdids, const char **mtdparts);
+
+static const char *get_mtdids(void)
+{
+	__maybe_unused const char *mtdparts = NULL;
+	const char *mtdids = env_get("mtdids");
+
+	if (mtdids)
+		return mtdids;
+
+#if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
+	board_mtdparts_default(&mtdids, &mtdparts);
+#elif defined(MTDIDS_DEFAULT)
+	mtdids = MTDIDS_DEFAULT;
+#elif defined(CONFIG_MTDIDS_DEFAULT)
+	mtdids = CONFIG_MTDIDS_DEFAULT;
+#endif
+
+	if (mtdids)
+		env_set("mtdids", mtdids);
+
+	return mtdids;
+}
 
 /**
  * mtd_search_alternate_name - Search an alternate name for @mtdname thanks to
@@ -34,7 +57,7 @@
 	const char *mtdids, *equal, *comma, *dev_id, *mtd_id;
 	int dev_id_len, mtd_id_len;
 
-	mtdids = env_get("mtdids");
+	mtdids = get_mtdids();
 	if (!mtdids)
 		return -EINVAL;
 
@@ -92,30 +115,6 @@
 #endif
 
 #if defined(CONFIG_MTD_PARTITIONS)
-extern void board_mtdparts_default(const char **mtdids,
-				   const char **mtdparts);
-
-static const char *get_mtdids(void)
-{
-	__maybe_unused const char *mtdparts = NULL;
-	const char *mtdids = env_get("mtdids");
-
-	if (mtdids)
-		return mtdids;
-
-#if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
-	board_mtdparts_default(&mtdids, &mtdparts);
-#elif defined(MTDIDS_DEFAULT)
-	mtdids = MTDIDS_DEFAULT;
-#elif defined(CONFIG_MTDIDS_DEFAULT)
-	mtdids = CONFIG_MTDIDS_DEFAULT;
-#endif
-
-	if (mtdids)
-		env_set("mtdids", mtdids);
-
-	return mtdids;
-}
 
 #define MTDPARTS_MAXLEN         512
 
@@ -150,20 +149,74 @@
 	return mtdparts;
 }
 
+static int mtd_del_parts(struct mtd_info *mtd, bool quiet)
+{
+	int ret;
+
+	if (!mtd_has_partitions(mtd))
+		return 0;
+
+	/* do not delete partitions if they are in use. */
+	if (mtd_partitions_used(mtd)) {
+		if (!quiet)
+			printf("\"%s\" partitions still in use, can't delete them\n",
+			       mtd->name);
+		return -EACCES;
+	}
+
+	ret = del_mtd_partitions(mtd);
+	if (ret)
+		return ret;
+
+	return 1;
+}
+
+static bool mtd_del_all_parts_failed;
+
+static void mtd_del_all_parts(void)
+{
+	struct mtd_info *mtd;
+	int ret = 0;
+
+	mtd_del_all_parts_failed = false;
+
+	/*
+	 * It is not safe to remove entries from the mtd_for_each_device loop
+	 * as it uses idr indexes and the partitions removal is done in bulk
+	 * (all partitions of one device at the same time), so break and
+	 * iterate from start each time a new partition is found and deleted.
+	 */
+	do {
+		mtd_for_each_device(mtd) {
+			ret = mtd_del_parts(mtd, false);
+			if (ret > 0)
+				break;
+			else if (ret < 0)
+				mtd_del_all_parts_failed = true;
+		}
+	} while (ret > 0);
+}
+
 int mtd_probe_devices(void)
 {
 	static char *old_mtdparts;
 	static char *old_mtdids;
 	const char *mtdparts = get_mtdparts();
 	const char *mtdids = get_mtdids();
-	bool remaining_partitions = true;
+	const char *mtdparts_next = mtdparts;
 	struct mtd_info *mtd;
 
 	mtd_probe_uclass_mtd_devs();
 
-	/* Check if mtdparts/mtdids changed since last call, otherwise: exit */
+	/*
+	 * Check if mtdparts/mtdids changed, if the MTD dev list was updated
+	 * or if our previous attempt to delete existing partititions failed.
+	 * In any of these cases we want to update the partitions, otherwise,
+	 * everything is up-to-date and we can return 0 directly.
+	 */
 	if ((!mtdparts && !old_mtdparts && !mtdids && !old_mtdids) ||
 	    (mtdparts && old_mtdparts && mtdids && old_mtdids &&
+	     !mtd_dev_list_updated() && !mtd_del_all_parts_failed &&
 	     !strcmp(mtdparts, old_mtdparts) &&
 	     !strcmp(mtdids, old_mtdids)))
 		return 0;
@@ -174,55 +227,55 @@
 	old_mtdparts = strdup(mtdparts);
 	old_mtdids = strdup(mtdids);
 
-	/* If at least one partition is still in use, do not delete anything */
-	mtd_for_each_device(mtd) {
-		if (mtd->usecount) {
-			printf("Partition \"%s\" already in use, aborting\n",
-			       mtd->name);
-			return -EACCES;
-		}
-	}
+	/*
+	 * Remove all old parts. Note that partition removal can fail in case
+	 * one of the partition is still being used by an MTD user, so this
+	 * does not guarantee that all old partitions are gone.
+	 */
+	mtd_del_all_parts();
 
 	/*
-	 * Everything looks clear, remove all partitions. It is not safe to
-	 * remove entries from the mtd_for_each_device loop as it uses idr
-	 * indexes and the partitions removal is done in bulk (all partitions of
-	 * one device at the same time), so break and iterate from start each
-	 * time a new partition is found and deleted.
+	 * Call mtd_dev_list_updated() to clear updates generated by our own
+	 * parts removal loop.
 	 */
-	while (remaining_partitions) {
-		remaining_partitions = false;
-		mtd_for_each_device(mtd) {
-			if (!mtd_is_partition(mtd) && mtd_has_partitions(mtd)) {
-				del_mtd_partitions(mtd);
-				remaining_partitions = true;
-				break;
-			}
-		}
-	}
+	mtd_dev_list_updated();
 
 	/* If either mtdparts or mtdids is empty, then exit */
 	if (!mtdparts || !mtdids)
 		return 0;
 
 	/* Start the parsing by ignoring the extra 'mtdparts=' prefix, if any */
-	if (strstr(mtdparts, "mtdparts="))
+	if (!strncmp(mtdparts, "mtdparts=", sizeof("mtdparts=") - 1))
 		mtdparts += 9;
 
 	/* For each MTD device in mtdparts */
-	while (mtdparts[0] != '\0') {
+	for (; mtdparts[0] != '\0'; mtdparts = mtdparts_next) {
 		char mtd_name[MTD_NAME_MAX_LEN], *colon;
 		struct mtd_partition *parts;
-		int mtd_name_len, nparts;
-		int ret;
+		unsigned int mtd_name_len;
+		int nparts, ret;
+
+		mtdparts_next = strchr(mtdparts, ';');
+		if (!mtdparts_next)
+			mtdparts_next = mtdparts + strlen(mtdparts);
+		else
+			mtdparts_next++;
 
 		colon = strchr(mtdparts, ':');
+		if (colon > mtdparts_next)
+			colon = NULL;
+
 		if (!colon) {
 			printf("Wrong mtdparts: %s\n", mtdparts);
 			return -EINVAL;
 		}
 
-		mtd_name_len = colon - mtdparts;
+		mtd_name_len = (unsigned int)(colon - mtdparts);
+		if (mtd_name_len + 1 > sizeof(mtd_name)) {
+			printf("MTD name too long: %s\n", mtdparts);
+			return -EINVAL;
+		}
+
 		strncpy(mtd_name, mtdparts, mtd_name_len);
 		mtd_name[mtd_name_len] = '\0';
 		/* Move the pointer forward (including the ':') */
@@ -249,15 +302,23 @@
 			if (ret || IS_ERR_OR_NULL(mtd)) {
 				printf("Could not find a valid device for %s\n",
 				       mtd_name);
-				mtdparts = strchr(mtdparts, ';');
-				if (mtdparts)
-					mtdparts++;
-
+				mtdparts = mtdparts_next;
 				continue;
 			}
 		}
 
 		/*
+		 * Call mtd_del_parts() again, even if it's already been called
+		 * in mtd_del_all_parts(). We need to know if old partitions are
+		 * still around (because they are still being used by someone),
+		 * and if they are, we shouldn't create new partitions, so just
+		 * skip this MTD device and try the next one.
+		 */
+		ret = mtd_del_parts(mtd, true);
+		if (ret < 0)
+			continue;
+
+		/*
 		 * Parse the MTD device partitions. It will update the mtdparts
 		 * pointer, create an array of parts (that must be freed), and
 		 * return the number of partition structures in the array.
@@ -281,6 +342,12 @@
 		put_mtd_device(mtd);
 	}
 
+	/*
+	 * Call mtd_dev_list_updated() to clear updates generated by our own
+	 * parts registration loop.
+	 */
+	mtd_dev_list_updated();
+
 	return 0;
 }
 #else
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index fb6c779..cb7ca38 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -87,14 +87,17 @@
 
 struct idr {
 	struct idr_layer id[MAX_IDR_ID];
+	bool updated;
 };
 
 #define DEFINE_IDR(name)	struct idr name;
 
 void idr_remove(struct idr *idp, int id)
 {
-	if (idp->id[id].used)
+	if (idp->id[id].used) {
 		idp->id[id].used = 0;
+		idp->updated = true;
+	}
 
 	return;
 }
@@ -134,6 +137,7 @@
 		if (idl->used == 0) {
 			idl->used = 1;
 			idl->ptr = ptr;
+			idp->updated = true;
 			return i;
 		}
 		i++;
@@ -155,6 +159,16 @@
 }
 EXPORT_SYMBOL_GPL(__mtd_next_device);
 
+bool mtd_dev_list_updated(void)
+{
+	if (mtd_idr.updated) {
+		mtd_idr.updated = false;
+		return true;
+	}
+
+	return false;
+}
+
 #ifndef __UBOOT__
 static LIST_HEAD(mtd_notifiers);
 
@@ -514,6 +528,13 @@
 	struct mtd_notifier *not;
 #endif
 
+	ret = del_mtd_partitions(mtd);
+	if (ret) {
+		debug("Failed to delete MTD partitions attached to %s (err %d)\n",
+		      mtd->name, ret);
+		return ret;
+	}
+
 	mutex_lock(&mtd_table_mutex);
 
 	if (idr_find(&mtd_idr, mtd->index) != mtd) {
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 4d2ac81..fd8d8e5 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -63,6 +63,18 @@
 #define MTD_SIZE_REMAINING		(~0LLU)
 #define MTD_OFFSET_NOT_SPECIFIED	(~0LLU)
 
+bool mtd_partitions_used(struct mtd_info *master)
+{
+	struct mtd_info *slave;
+
+	list_for_each_entry(slave, &master->partitions, node) {
+		if (slave->usecount)
+			return true;
+	}
+
+	return false;
+}
+
 /**
  * mtd_parse_partition - Parse @mtdparts partition definition, fill @partition
  *                       with it and update the @mtdparts string pointer.
diff --git a/drivers/mtd/nand/raw/pxa3xx_nand.c b/drivers/mtd/nand/raw/pxa3xx_nand.c
index 4c783f1..4d2712d 100644
--- a/drivers/mtd/nand/raw/pxa3xx_nand.c
+++ b/drivers/mtd/nand/raw/pxa3xx_nand.c
@@ -195,6 +195,7 @@
 
 	int			cs;
 	int			use_ecc;	/* use HW ECC ? */
+	int			force_raw;	/* prevent use_ecc to be set */
 	int			ecc_bch;	/* using BCH ECC? */
 	int			use_spare;	/* use spare ? */
 	int			need_wait;
@@ -326,14 +327,14 @@
 static struct nand_ecclayout ecc_layout_2KB_bch8bit = {
 	.eccbytes = 64,
 	.eccpos = {
-		64,  65,  66,  67,  68,  69,  70,  71,
-		72,  73,  74,  75,  76,  77,  78,  79,
-		80,  81,  82,  83,  84,  85,  86,  87,
-		88,  89,  90,  91,  92,  93,  94,  95,
-		96,  97,  98,  99,  100, 101, 102, 103,
-		104, 105, 106, 107, 108, 109, 110, 111,
-		112, 113, 114, 115, 116, 117, 118, 119,
-		120, 121, 122, 123, 124, 125, 126, 127},
+		32, 33, 34, 35, 36, 37, 38, 39,
+		40, 41, 42, 43, 44, 45, 46, 47,
+		48, 49, 50, 51, 52, 53, 54, 55,
+		56, 57, 58, 59, 60, 61, 62, 63,
+		64, 65, 66, 67, 68, 69, 70, 71,
+		72, 73, 74, 75, 76, 77, 78, 79,
+		80, 81, 82, 83, 84, 85, 86, 87,
+		88, 89, 90, 91, 92, 93, 94, 95},
 	.oobfree = { {1, 4}, {6, 26} }
 };
 
@@ -579,7 +580,7 @@
 
 static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
 {
-	if (info->ecc_bch) {
+	if (info->ecc_bch && !info->force_raw) {
 		u32 ts;
 
 		/*
@@ -612,12 +613,22 @@
 
 static void handle_data_pio(struct pxa3xx_nand_info *info)
 {
+	int data_len = info->step_chunk_size;
+
+	/*
+	 * In raw mode, include the spare area and the ECC bytes that are not
+	 * consumed by the controller in the data section. Do not reorganize
+	 * here, do it in the ->read_page_raw() handler instead.
+	 */
+	if (info->force_raw)
+		data_len += info->step_spare_size + info->ecc_size;
+
 	switch (info->state) {
 	case STATE_PIO_WRITING:
 		if (info->step_chunk_size)
 			writesl(info->mmio_base + NDDB,
 				info->data_buff + info->data_buff_pos,
-				DIV_ROUND_UP(info->step_chunk_size, 4));
+				DIV_ROUND_UP(data_len, 4));
 
 		if (info->step_spare_size)
 			writesl(info->mmio_base + NDDB,
@@ -628,7 +639,10 @@
 		if (info->step_chunk_size)
 			drain_fifo(info,
 				   info->data_buff + info->data_buff_pos,
-				   DIV_ROUND_UP(info->step_chunk_size, 4));
+				   DIV_ROUND_UP(data_len, 4));
+
+		if (info->force_raw)
+			break;
 
 		if (info->step_spare_size)
 			drain_fifo(info,
@@ -642,7 +656,7 @@
 	}
 
 	/* Update buffer pointers for multi-page read/write */
-	info->data_buff_pos += info->step_chunk_size;
+	info->data_buff_pos += data_len;
 	info->oob_buff_pos += info->step_spare_size;
 }
 
@@ -796,7 +810,8 @@
 	case NAND_CMD_READ0:
 	case NAND_CMD_READOOB:
 	case NAND_CMD_PAGEPROG:
-		info->use_ecc = 1;
+		if (!info->force_raw)
+			info->use_ecc = 1;
 		break;
 	case NAND_CMD_PARAM:
 		info->use_spare = 0;
@@ -866,7 +881,13 @@
 		 * which is either naked-read or last-read according to the
 		 * state.
 		 */
-		if (mtd->writesize == info->chunk_size) {
+		if (info->force_raw) {
+			info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8) |
+				       NDCB0_LEN_OVRD |
+				       NDCB0_EXT_CMD_TYPE(ext_cmd_type);
+			info->ndcb3 = info->step_chunk_size +
+				      info->step_spare_size + info->ecc_size;
+		} else if (mtd->writesize == info->chunk_size) {
 			info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8);
 		} else if (mtd->writesize > info->chunk_size) {
 			info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8)
@@ -1216,6 +1237,7 @@
 {
 	struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
 	struct pxa3xx_nand_info *info = host->info_data;
+	int bf;
 
 	chip->read_buf(mtd, buf, mtd->writesize);
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -1223,12 +1245,30 @@
 	if (info->retcode == ERR_CORERR && info->use_ecc) {
 		mtd->ecc_stats.corrected += info->ecc_err_cnt;
 
-	} else if (info->retcode == ERR_UNCORERR) {
+	} else if (info->retcode == ERR_UNCORERR && info->ecc_bch) {
 		/*
-		 * for blank page (all 0xff), HW will calculate its ECC as
-		 * 0, which is different from the ECC information within
-		 * OOB, ignore such uncorrectable errors
+		 * Empty pages will trigger uncorrectable errors. Re-read the
+		 * entire page in raw mode and check for bits not being "1".
+		 * If there are more than the supported strength, then it means
+		 * this is an actual uncorrectable error.
 		 */
+		chip->ecc.read_page_raw(mtd, chip, buf, oob_required, page);
+		bf = nand_check_erased_ecc_chunk(buf, mtd->writesize,
+						 chip->oob_poi, mtd->oobsize,
+						 NULL, 0, chip->ecc.strength);
+		if (bf < 0) {
+			mtd->ecc_stats.failed++;
+		} else if (bf) {
+			mtd->ecc_stats.corrected += bf;
+			info->max_bitflips = max_t(unsigned int,
+						   info->max_bitflips, bf);
+			info->retcode = ERR_CORERR;
+		} else {
+			info->retcode = ERR_NONE;
+		}
+
+	} else if (info->retcode == ERR_UNCORERR && !info->ecc_bch) {
+		/* Raw read is not supported with Hamming ECC engine */
 		if (is_buf_blank(buf, mtd->writesize))
 			info->retcode = ERR_NONE;
 		else
@@ -1238,6 +1278,69 @@
 	return info->max_bitflips;
 }
 
+static int pxa3xx_nand_read_page_raw(struct mtd_info *mtd,
+				     struct nand_chip *chip, uint8_t *buf,
+				     int oob_required, int page)
+{
+	struct pxa3xx_nand_host *host = chip->priv;
+	struct pxa3xx_nand_info *info = host->info_data;
+	int chunk, ecc_off_buf;
+
+	if (!info->ecc_bch)
+		return -ENOTSUPP;
+
+	/*
+	 * Set the force_raw boolean, then re-call ->cmdfunc() that will run
+	 * pxa3xx_nand_start(), which will actually disable the ECC engine.
+	 */
+	info->force_raw = true;
+	chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+
+	ecc_off_buf = (info->nfullchunks * info->spare_size) +
+		      info->last_spare_size;
+	for (chunk = 0; chunk < info->nfullchunks; chunk++) {
+		chip->read_buf(mtd,
+			       buf + (chunk * info->chunk_size),
+			       info->chunk_size);
+		chip->read_buf(mtd,
+			       chip->oob_poi +
+			       (chunk * (info->spare_size)),
+			       info->spare_size);
+		chip->read_buf(mtd,
+			       chip->oob_poi + ecc_off_buf +
+			       (chunk * (info->ecc_size)),
+			       info->ecc_size - 2);
+	}
+
+	if (info->ntotalchunks > info->nfullchunks) {
+		chip->read_buf(mtd,
+			       buf + (info->nfullchunks * info->chunk_size),
+			       info->last_chunk_size);
+		chip->read_buf(mtd,
+			       chip->oob_poi +
+			       (info->nfullchunks * (info->spare_size)),
+			       info->last_spare_size);
+		chip->read_buf(mtd,
+			       chip->oob_poi + ecc_off_buf +
+			       (info->nfullchunks * (info->ecc_size)),
+			       info->ecc_size - 2);
+	}
+
+	info->force_raw = false;
+
+	return 0;
+}
+
+static int pxa3xx_nand_read_oob_raw(struct mtd_info *mtd,
+				    struct nand_chip *chip, int page)
+{
+	/* Invalidate page cache */
+	chip->pagebuf = -1;
+
+	return chip->ecc.read_page_raw(mtd, chip, chip->buffers->databuf, true,
+				       page);
+}
+
 static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
 {
 	struct nand_chip *chip = mtd_to_nand(mtd);
@@ -1488,7 +1591,7 @@
 		info->chunk_size = 1024;
 		info->spare_size = 0;
 		info->last_chunk_size = 1024;
-		info->last_spare_size = 64;
+		info->last_spare_size = 32;
 		info->ecc_size = 32;
 		ecc->mode = NAND_ECC_HW;
 		ecc->size = info->chunk_size;
@@ -1669,6 +1772,8 @@
 
 		nand_set_controller_data(chip, host);
 		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.read_page_raw	= pxa3xx_nand_read_page_raw;
+		chip->ecc.read_oob_raw	= pxa3xx_nand_read_oob_raw;
 		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
 		chip->controller        = &info->controller;
 		chip->waitfunc		= pxa3xx_nand_waitfunc;
diff --git a/drivers/mtd/spi/sf_mtd.c b/drivers/mtd/spi/sf_mtd.c
index 58d7e44..68c3600 100644
--- a/drivers/mtd/spi/sf_mtd.c
+++ b/drivers/mtd/spi/sf_mtd.c
@@ -10,6 +10,7 @@
 #include <spi_flash.h>
 
 static struct mtd_info sf_mtd_info;
+static bool sf_mtd_registered;
 static char sf_mtd_name[8];
 
 static int spi_flash_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
@@ -17,6 +18,9 @@
 	struct spi_flash *flash = mtd->priv;
 	int err;
 
+	if (!flash)
+		return -ENODEV;
+
 	instr->state = MTD_ERASING;
 
 	err = spi_flash_erase(flash, instr->addr, instr->len);
@@ -38,6 +42,9 @@
 	struct spi_flash *flash = mtd->priv;
 	int err;
 
+	if (!flash)
+		return -ENODEV;
+
 	err = spi_flash_read(flash, from, len, buf);
 	if (!err)
 		*retlen = len;
@@ -51,6 +58,9 @@
 	struct spi_flash *flash = mtd->priv;
 	int err;
 
+	if (!flash)
+		return -ENODEV;
+
 	err = spi_flash_write(flash, to, len, buf);
 	if (!err)
 		*retlen = len;
@@ -73,6 +83,17 @@
 
 int spi_flash_mtd_register(struct spi_flash *flash)
 {
+	int ret;
+
+	if (sf_mtd_registered) {
+		ret = del_mtd_device(&sf_mtd_info);
+		if (ret)
+			return ret;
+
+		sf_mtd_registered = false;
+	}
+
+	sf_mtd_registered = false;
 	memset(&sf_mtd_info, 0, sizeof(sf_mtd_info));
 	sprintf(sf_mtd_name, "nor%d", spi_flash_mtd_number());
 
@@ -94,10 +115,33 @@
 	sf_mtd_info.numeraseregions = 0;
 	sf_mtd_info.erasesize = flash->sector_size;
 
-	return add_mtd_device(&sf_mtd_info);
+	ret = add_mtd_device(&sf_mtd_info);
+	if (!ret)
+		sf_mtd_registered = true;
+
+	return ret;
 }
 
 void spi_flash_mtd_unregister(void)
 {
-	del_mtd_device(&sf_mtd_info);
+	int ret;
+
+	if (!sf_mtd_registered)
+		return;
+
+	ret = del_mtd_device(&sf_mtd_info);
+	if (!ret) {
+		sf_mtd_registered = false;
+		return;
+	}
+
+	/*
+	 * Setting mtd->priv to NULL is the best we can do. Thanks to that,
+	 * the MTD layer can still call mtd hooks without risking a
+	 * use-after-free bug. Still, things should be fixed to prevent the
+	 * spi_flash object from being destroyed when del_mtd_device() fails.
+	 */
+	sf_mtd_info.priv = NULL;
+	printf("Failed to unregister MTD %s and the spi_flash object is going away: you're in deep trouble!",
+	       sf_mtd_info.name);
 }
diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c
index 5a2e932..00f8558 100644
--- a/drivers/mtd/spi/sf_probe.c
+++ b/drivers/mtd/spi/sf_probe.c
@@ -144,6 +144,14 @@
 	return spi_flash_probe_slave(flash);
 }
 
+static int spi_flash_std_remove(struct udevice *dev)
+{
+#ifdef CONFIG_SPI_FLASH_MTD
+	spi_flash_mtd_unregister();
+#endif
+	return 0;
+}
+
 static const struct dm_spi_flash_ops spi_flash_std_ops = {
 	.read = spi_flash_std_read,
 	.write = spi_flash_std_write,
@@ -161,6 +169,7 @@
 	.id		= UCLASS_SPI_FLASH,
 	.of_match	= spi_flash_std_ids,
 	.probe		= spi_flash_std_probe,
+	.remove		= spi_flash_std_remove,
 	.priv_auto_alloc_size = sizeof(struct spi_flash),
 	.ops		= &spi_flash_std_ops,
 };
diff --git a/drivers/net/e1000_spi.c b/drivers/net/e1000_spi.c
index b38f4df..aecd290 100644
--- a/drivers/net/e1000_spi.c
+++ b/drivers/net/e1000_spi.c
@@ -77,9 +77,6 @@
 	return container_of(spi, struct e1000_hw, spi);
 }
 
-/* Not sure why all of these are necessary */
-void spi_init(void)   { /* Nothing to do */ }
-
 struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
 		unsigned int max_hz, unsigned int mode)
 {
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 516188e..a7bb5b3 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -116,6 +116,20 @@
 	  access the SPI NOR flash on platforms embedding this Intel
 	  ICH IP core.
 
+config MESON_SPIFC
+	bool "Amlogic Meson SPI Flash Controller driver"
+	depends on ARCH_MESON
+	help
+	  Enable the Amlogic Meson SPI Flash Controller SPIFC) driver.
+	  This driver can be used to access the SPI NOR flash chips on
+	  Amlogic Meson SoCs.
+
+config MPC8XX_SPI
+	bool "MPC8XX SPI Driver"
+	depends on MPC8xx
+	help
+	  Enable support for SPI on MPC8XX
+
 config MT7621_SPI
 	bool "MediaTek MT7621 SPI driver"
 	depends on ARCH_MT7620
@@ -124,6 +138,13 @@
 	  the SPI NOR flash on platforms embedding this Ralink / MediaTek
 	  SPI core, like MT7621/7628/7688.
 
+config MTK_QSPI
+	bool "Mediatek QSPI driver"
+	help
+	  Enable the Mediatek QSPI driver. This driver can be
+	  used to access the SPI NOR flash on platforms embedding this
+	  Mediatek QSPI IP core.
+
 config MVEBU_A3700_SPI
 	bool "Marvell Armada 3700 SPI driver"
 	select CLK_ARMADA_3720
@@ -328,12 +349,6 @@
 	help
 	  Enable support for SPI on LPC32xx
 
-config MPC8XX_SPI
-	bool "MPC8XX SPI Driver"
-	depends on MPC8xx
-	help
-	  Enable support for SPI on MPC8XX
-
 config MPC8XXX_SPI
 	bool "MPC8XXX SPI Driver"
 	help
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 7242ea7..392a925 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -31,8 +31,10 @@
 obj-$(CONFIG_ICH_SPI) +=  ich.o
 obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
 obj-$(CONFIG_LPC32XX_SSP) += lpc32xx_ssp.o
+obj-$(CONFIG_MESON_SPIFC) += meson_spifc.o
 obj-$(CONFIG_MPC8XX_SPI) += mpc8xx_spi.o
 obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
+obj-$(CONFIG_MTK_QSPI) += mtk_qspi.o
 obj-$(CONFIG_MT7621_SPI) += mt7621_spi.o
 obj-$(CONFIG_MVEBU_A3700_SPI) += mvebu_a3700_spi.o
 obj-$(CONFIG_MXC_SPI) += mxc_spi.o
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index 1db8bbe..cf4de9e 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -34,11 +34,6 @@
 	return (ATMEL_SPI_VERSION_REV(ver) >= 0x210);
 }
 
-void spi_init()
-{
-
-}
-
 struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
 			unsigned int max_hz, unsigned int mode)
 {
diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c
index 07fa5e3..4d2c106 100644
--- a/drivers/spi/davinci_spi.c
+++ b/drivers/spi/davinci_spi.c
@@ -388,11 +388,6 @@
 	/* do nothing */
 }
 
-void spi_init(void)
-{
-	/* do nothing */
-}
-
 struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
 			unsigned int max_hz, unsigned int mode)
 {
diff --git a/drivers/spi/fsl_dspi.c b/drivers/spi/fsl_dspi.c
index f7ed8fb..764c942 100644
--- a/drivers/spi/fsl_dspi.c
+++ b/drivers/spi/fsl_dspi.c
@@ -390,11 +390,6 @@
 	return 0;
 }
 #ifndef CONFIG_DM_SPI
-void spi_init(void)
-{
-	/* Nothing to do */
-}
-
 int spi_cs_is_valid(unsigned int bus, unsigned int cs)
 {
 	if (((cs >= 0) && (cs < 8)) && ((bus >= 0) && (bus < 8)))
diff --git a/drivers/spi/fsl_espi.c b/drivers/spi/fsl_espi.c
index e994159..7444ae1 100644
--- a/drivers/spi/fsl_espi.c
+++ b/drivers/spi/fsl_espi.c
@@ -118,11 +118,6 @@
 	free(fsl);
 }
 
-void spi_init(void)
-{
-
-}
-
 int spi_claim_bus(struct spi_slave *slave)
 {
 	struct fsl_spi_slave *fsl = to_fsl_spi_slave(slave);
diff --git a/drivers/spi/lpc32xx_ssp.c b/drivers/spi/lpc32xx_ssp.c
index ce12eee..4b09366 100644
--- a/drivers/spi/lpc32xx_ssp.c
+++ b/drivers/spi/lpc32xx_ssp.c
@@ -47,15 +47,6 @@
 	return container_of(slave, struct lpc32xx_spi_slave, slave);
 }
 
-/* spi_init is called during boot when CONFIG_CMD_SPI is defined */
-void spi_init(void)
-{
-	/*
-	 *  nothing to do: clocking was enabled in lpc32xx_ssp_enable()
-	 * and configuration will be done in spi_setup_slave()
-	*/
-}
-
 /* the following is called in sequence by do_spi_xfer() */
 
 struct spi_slave *spi_setup_slave(uint bus, uint cs, uint max_hz, uint mode)
diff --git a/drivers/spi/meson_spifc.c b/drivers/spi/meson_spifc.c
new file mode 100644
index 0000000..3d55169
--- /dev/null
+++ b/drivers/spi/meson_spifc.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
+ * Copyright (C) 2018 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ *
+ * Amlogic Meson SPI Flash Controller driver
+ */
+
+#include <common.h>
+#include <spi.h>
+#include <clk.h>
+#include <dm.h>
+#include <regmap.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <linux/bitfield.h>
+
+/* register map */
+#define REG_CMD			0x00
+#define REG_ADDR		0x04
+#define REG_CTRL		0x08
+#define REG_CTRL1		0x0c
+#define REG_STATUS		0x10
+#define REG_CTRL2		0x14
+#define REG_CLOCK		0x18
+#define REG_USER		0x1c
+#define REG_USER1		0x20
+#define REG_USER2		0x24
+#define REG_USER3		0x28
+#define REG_USER4		0x2c
+#define REG_SLAVE		0x30
+#define REG_SLAVE1		0x34
+#define REG_SLAVE2		0x38
+#define REG_SLAVE3		0x3c
+#define REG_C0			0x40
+#define REG_B8			0x60
+#define REG_MAX			0x7c
+
+/* register fields */
+#define CMD_USER		BIT(18)
+#define CTRL_ENABLE_AHB		BIT(17)
+#define CLOCK_SOURCE		BIT(31)
+#define CLOCK_DIV_SHIFT		12
+#define CLOCK_DIV_MASK		(0x3f << CLOCK_DIV_SHIFT)
+#define CLOCK_CNT_HIGH_SHIFT	6
+#define CLOCK_CNT_HIGH_MASK	(0x3f << CLOCK_CNT_HIGH_SHIFT)
+#define CLOCK_CNT_LOW_SHIFT	0
+#define CLOCK_CNT_LOW_MASK	(0x3f << CLOCK_CNT_LOW_SHIFT)
+#define USER_DIN_EN_MS		BIT(0)
+#define USER_CMP_MODE		BIT(2)
+#define USER_CLK_NOT_INV	BIT(7)
+#define USER_UC_DOUT_SEL	BIT(27)
+#define USER_UC_DIN_SEL		BIT(28)
+#define USER_UC_MASK		((BIT(5) - 1) << 27)
+#define USER1_BN_UC_DOUT_SHIFT	17
+#define USER1_BN_UC_DOUT_MASK	(0xff << 16)
+#define USER1_BN_UC_DIN_SHIFT	8
+#define USER1_BN_UC_DIN_MASK	(0xff << 8)
+#define USER4_CS_POL_HIGH	BIT(23)
+#define USER4_IDLE_CLK_HIGH	BIT(29)
+#define USER4_CS_ACT		BIT(30)
+#define SLAVE_TRST_DONE		BIT(4)
+#define SLAVE_OP_MODE		BIT(30)
+#define SLAVE_SW_RST		BIT(31)
+
+#define SPIFC_BUFFER_SIZE	64
+
+struct meson_spifc_priv {
+	struct regmap			*regmap;
+	struct clk			clk;
+};
+
+/**
+ * meson_spifc_drain_buffer() - copy data from device buffer to memory
+ * @spifc:	the Meson SPI device
+ * @buf:	the destination buffer
+ * @len:	number of bytes to copy
+ */
+static void meson_spifc_drain_buffer(struct meson_spifc_priv *spifc,
+				     u8 *buf, int len)
+{
+	u32 data;
+	int i = 0;
+
+	while (i < len) {
+		regmap_read(spifc->regmap, REG_C0 + i, &data);
+
+		if (len - i >= 4) {
+			*((u32 *)buf) = data;
+			buf += 4;
+		} else {
+			memcpy(buf, &data, len - i);
+			break;
+		}
+		i += 4;
+	}
+}
+
+/**
+ * meson_spifc_fill_buffer() - copy data from memory to device buffer
+ * @spifc:	the Meson SPI device
+ * @buf:	the source buffer
+ * @len:	number of bytes to copy
+ */
+static void meson_spifc_fill_buffer(struct meson_spifc_priv *spifc,
+				    const u8 *buf, int len)
+{
+	u32 data = 0;
+	int i = 0;
+
+	while (i < len) {
+		if (len - i >= 4)
+			data = *(u32 *)buf;
+		else
+			memcpy(&data, buf, len - i);
+
+		regmap_write(spifc->regmap, REG_C0 + i, data);
+
+		buf += 4;
+		i += 4;
+	}
+}
+
+/**
+ * meson_spifc_txrx() - transfer a chunk of data
+ * @spifc:	the Meson SPI device
+ * @dout:	data buffer for TX
+ * @din:	data buffer for RX
+ * @offset:	offset of the data to transfer
+ * @len:	length of the data to transfer
+ * @last_xfer:	whether this is the last transfer of the message
+ * @last_chunk:	whether this is the last chunk of the transfer
+ * Return:	0 on success, a negative value on error
+ */
+static int meson_spifc_txrx(struct meson_spifc_priv *spifc,
+			    const u8 *dout, u8 *din, int offset,
+			    int len, bool last_xfer, bool last_chunk)
+{
+	bool keep_cs = true;
+	u32 data;
+	int ret;
+
+	if (dout)
+		meson_spifc_fill_buffer(spifc, dout + offset, len);
+
+	/* enable DOUT stage */
+	regmap_update_bits(spifc->regmap, REG_USER, USER_UC_MASK,
+			   USER_UC_DOUT_SEL);
+	regmap_write(spifc->regmap, REG_USER1,
+		     (8 * len - 1) << USER1_BN_UC_DOUT_SHIFT);
+
+	/* enable data input during DOUT */
+	regmap_update_bits(spifc->regmap, REG_USER, USER_DIN_EN_MS,
+			   USER_DIN_EN_MS);
+
+	if (last_chunk && last_xfer)
+		keep_cs = false;
+
+	regmap_update_bits(spifc->regmap, REG_USER4, USER4_CS_ACT,
+			   keep_cs ? USER4_CS_ACT : 0);
+
+	/* clear transition done bit */
+	regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_TRST_DONE, 0);
+	/* start transfer */
+	regmap_update_bits(spifc->regmap, REG_CMD, CMD_USER, CMD_USER);
+
+	/* wait for the current operation to terminate */
+	ret = regmap_read_poll_timeout(spifc->regmap, REG_SLAVE, data,
+				       (data & SLAVE_TRST_DONE),
+				       0, 5 * CONFIG_SYS_HZ);
+
+	if (!ret && din)
+		meson_spifc_drain_buffer(spifc, din + offset, len);
+
+	return ret;
+}
+
+/**
+ * meson_spifc_xfer() - perform a single transfer
+ * @dev:	the SPI controller device
+ * @bitlen:	length of the transfer
+ * @dout:	data buffer for TX
+ * @din:	data buffer for RX
+ * @flags:	transfer flags
+ * Return:	0 on success, a negative value on error
+ */
+static int meson_spifc_xfer(struct udevice *slave, unsigned int bitlen,
+			    const void *dout, void *din, unsigned long flags)
+{
+	struct meson_spifc_priv *spifc = dev_get_priv(slave->parent);
+	int blen = bitlen / 8;
+	int len, done = 0, ret = 0;
+
+	if (bitlen % 8)
+		return -EINVAL;
+
+	debug("xfer len %d (%d) dout %p din %p\n", bitlen, blen, dout, din);
+
+	regmap_update_bits(spifc->regmap, REG_CTRL, CTRL_ENABLE_AHB, 0);
+
+	while (done < blen && !ret) {
+		len = min_t(int, blen - done, SPIFC_BUFFER_SIZE);
+		ret = meson_spifc_txrx(spifc, dout, din, done, len,
+				       flags & SPI_XFER_END,
+				       done + len >= blen);
+		done += len;
+	}
+
+	regmap_update_bits(spifc->regmap, REG_CTRL, CTRL_ENABLE_AHB,
+			   CTRL_ENABLE_AHB);
+
+	return ret;
+}
+
+/**
+ * meson_spifc_set_speed() - program the clock divider
+ * @dev:	the SPI controller device
+ * @speed:	desired speed in Hz
+ */
+static int meson_spifc_set_speed(struct udevice *dev, uint speed)
+{
+	struct meson_spifc_priv *spifc = dev_get_priv(dev);
+	unsigned long parent, value;
+	int n;
+
+	parent = clk_get_rate(&spifc->clk);
+	n = max_t(int, parent / speed - 1, 1);
+
+	debug("parent %lu, speed %u, n %d\n", parent, speed, n);
+
+	value = (n << CLOCK_DIV_SHIFT) & CLOCK_DIV_MASK;
+	value |= (n << CLOCK_CNT_LOW_SHIFT) & CLOCK_CNT_LOW_MASK;
+	value |= (((n + 1) / 2 - 1) << CLOCK_CNT_HIGH_SHIFT) &
+		CLOCK_CNT_HIGH_MASK;
+
+	regmap_write(spifc->regmap, REG_CLOCK, value);
+
+	return 0;
+}
+
+/**
+ * meson_spifc_set_mode() - setups the SPI bus mode
+ * @dev:	the SPI controller device
+ * @mode:	desired mode bitfield
+ * Return:	0 on success, -ENODEV on error
+ */
+static int meson_spifc_set_mode(struct udevice *dev, uint mode)
+{
+	struct meson_spifc_priv *spifc = dev_get_priv(dev);
+
+	if (mode & (SPI_CPHA | SPI_RX_QUAD | SPI_RX_DUAL |
+		    SPI_TX_QUAD | SPI_TX_DUAL))
+		return -ENODEV;
+
+	regmap_update_bits(spifc->regmap, REG_USER, USER_CLK_NOT_INV,
+			   mode & SPI_CPOL ? USER_CLK_NOT_INV : 0);
+
+	regmap_update_bits(spifc->regmap, REG_USER4, USER4_CS_POL_HIGH,
+			   mode & SPI_CS_HIGH ? USER4_CS_POL_HIGH : 0);
+
+	return 0;
+}
+
+/**
+ * meson_spifc_hw_init() - reset and initialize the SPI controller
+ * @spifc:	the Meson SPI device
+ */
+static void meson_spifc_hw_init(struct meson_spifc_priv *spifc)
+{
+	/* reset device */
+	regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_SW_RST,
+			   SLAVE_SW_RST);
+	/* disable compatible mode */
+	regmap_update_bits(spifc->regmap, REG_USER, USER_CMP_MODE, 0);
+	/* set master mode */
+	regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_OP_MODE, 0);
+}
+
+static const struct dm_spi_ops meson_spifc_ops = {
+	.xfer		= meson_spifc_xfer,
+	.set_speed	= meson_spifc_set_speed,
+	.set_mode	= meson_spifc_set_mode,
+};
+
+static int meson_spifc_probe(struct udevice *dev)
+{
+	struct meson_spifc_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
+	if (ret)
+		return ret;
+
+	ret = clk_get_by_index(dev, 0, &priv->clk);
+	if (ret)
+		return ret;
+
+	ret = clk_enable(&priv->clk);
+	if (ret)
+		return ret;
+
+	meson_spifc_hw_init(priv);
+
+	return 0;
+}
+
+static const struct udevice_id meson_spifc_ids[] = {
+	{ .compatible = "amlogic,meson-gxbb-spifc", },
+	{ }
+};
+
+U_BOOT_DRIVER(meson_spifc) = {
+	.name		= "meson_spifc",
+	.id		= UCLASS_SPI,
+	.of_match	= meson_spifc_ids,
+	.ops		= &meson_spifc_ops,
+	.probe		= meson_spifc_probe,
+	.priv_auto_alloc_size = sizeof(struct meson_spifc_priv),
+};
diff --git a/drivers/spi/mtk_qspi.c b/drivers/spi/mtk_qspi.c
new file mode 100644
index 0000000..b510733
--- /dev/null
+++ b/drivers/spi/mtk_qspi.c
@@ -0,0 +1,359 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018  MediaTek, Inc.
+ * Author : Guochun.Mao@mediatek.com
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <malloc.h>
+#include <spi.h>
+#include <asm/io.h>
+#include <linux/iopoll.h>
+#include <linux/ioport.h>
+
+/* Register Offset */
+struct mtk_qspi_regs {
+	u32 cmd;
+	u32 cnt;
+	u32 rdsr;
+	u32 rdata;
+	u32 radr[3];
+	u32 wdata;
+	u32 prgdata[6];
+	u32 shreg[10];
+	u32 cfg[2];
+	u32 shreg10;
+	u32 mode_mon;
+	u32 status[4];
+	u32 flash_time;
+	u32 flash_cfg;
+	u32 reserved_0[3];
+	u32 sf_time;
+	u32 pp_dw_data;
+	u32 reserved_1;
+	u32 delsel_0[2];
+	u32 intrstus;
+	u32 intren;
+	u32 reserved_2;
+	u32 cfg3;
+	u32 reserved_3;
+	u32 chksum;
+	u32 aaicmd;
+	u32 wrprot;
+	u32 radr3;
+	u32 dual;
+	u32 delsel_1[3];
+};
+
+struct mtk_qspi_platdata {
+	fdt_addr_t reg_base;
+	fdt_addr_t mem_base;
+};
+
+struct mtk_qspi_priv {
+	struct mtk_qspi_regs *regs;
+	unsigned long *mem_base;
+	u8 op;
+	u8 tx[3]; /* only record max 3 bytes paras, when it's address. */
+	u32 txlen; /* dout buffer length  - op code length */
+	u8 *rx;
+	u32 rxlen;
+};
+
+#define MTK_QSPI_CMD_POLLINGREG_US 500000
+#define MTK_QSPI_WRBUF_SIZE        256
+#define MTK_QSPI_COMMAND_ENABLE    0x30
+
+/* NOR flash controller commands */
+#define MTK_QSPI_RD_TRIGGER        BIT(0)
+#define MTK_QSPI_READSTATUS        BIT(1)
+#define MTK_QSPI_PRG_CMD           BIT(2)
+#define MTK_QSPI_WR_TRIGGER        BIT(4)
+#define MTK_QSPI_WRITESTATUS       BIT(5)
+#define MTK_QSPI_AUTOINC           BIT(7)
+
+#define MTK_QSPI_MAX_RX_TX_SHIFT   0x6
+#define MTK_QSPI_MAX_SHIFT         0x8
+
+#define MTK_QSPI_WR_BUF_ENABLE     0x1
+#define MTK_QSPI_WR_BUF_DISABLE    0x0
+
+static int mtk_qspi_execute_cmd(struct mtk_qspi_priv *priv, u8 cmd)
+{
+	u8 tmp;
+	u8 val = cmd & ~MTK_QSPI_AUTOINC;
+
+	writeb(cmd, &priv->regs->cmd);
+
+	return readb_poll_timeout(&priv->regs->cmd, tmp, !(val & tmp),
+				  MTK_QSPI_CMD_POLLINGREG_US);
+}
+
+static int mtk_qspi_tx_rx(struct mtk_qspi_priv *priv)
+{
+	int len = 1 + priv->txlen + priv->rxlen;
+	int i, ret, idx;
+
+	if (len > MTK_QSPI_MAX_SHIFT)
+		return -ERR_INVAL;
+
+	writeb(len * 8, &priv->regs->cnt);
+
+	/* start at PRGDATA5, go down to PRGDATA0 */
+	idx = MTK_QSPI_MAX_RX_TX_SHIFT - 1;
+
+	/* opcode */
+	writeb(priv->op, &priv->regs->prgdata[idx]);
+	idx--;
+
+	/* program TX data */
+	for (i = 0; i < priv->txlen; i++, idx--)
+		writeb(priv->tx[i], &priv->regs->prgdata[idx]);
+
+	/* clear out rest of TX registers */
+	while (idx >= 0) {
+		writeb(0, &priv->regs->prgdata[idx]);
+		idx--;
+	}
+
+	ret = mtk_qspi_execute_cmd(priv, MTK_QSPI_PRG_CMD);
+	if (ret)
+		return ret;
+
+	/* restart at first RX byte */
+	idx = priv->rxlen - 1;
+
+	/* read out RX data */
+	for (i = 0; i < priv->rxlen; i++, idx--)
+		priv->rx[i] = readb(&priv->regs->shreg[idx]);
+
+	return 0;
+}
+
+static int mtk_qspi_read(struct mtk_qspi_priv *priv,
+			 u32 addr, u8 *buf, u32 len)
+{
+	memcpy(buf, (u8 *)priv->mem_base + addr, len);
+	return 0;
+}
+
+static void mtk_qspi_set_addr(struct mtk_qspi_priv *priv, u32 addr)
+{
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		writeb(addr & 0xff, &priv->regs->radr[i]);
+		addr >>= 8;
+	}
+}
+
+static int mtk_qspi_write_single_byte(struct mtk_qspi_priv *priv,
+				      u32 addr, u32 length, const u8 *data)
+{
+	int i, ret;
+
+	mtk_qspi_set_addr(priv, addr);
+
+	for (i = 0; i < length; i++) {
+		writeb(*data++, &priv->regs->wdata);
+		ret = mtk_qspi_execute_cmd(priv, MTK_QSPI_WR_TRIGGER);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+static int mtk_qspi_write_buffer(struct mtk_qspi_priv *priv, u32 addr,
+				 const u8 *buf)
+{
+	int i, data;
+
+	mtk_qspi_set_addr(priv, addr);
+
+	for (i = 0; i < MTK_QSPI_WRBUF_SIZE; i += 4) {
+		data = buf[i + 3] << 24 | buf[i + 2] << 16 |
+		       buf[i + 1] << 8 | buf[i];
+		writel(data, &priv->regs->pp_dw_data);
+	}
+
+	return mtk_qspi_execute_cmd(priv, MTK_QSPI_WR_TRIGGER);
+}
+
+static int mtk_qspi_write(struct mtk_qspi_priv *priv,
+			  u32 addr, const u8 *buf, u32 len)
+{
+	int ret;
+
+	/* setting pre-fetch buffer for page program */
+	writel(MTK_QSPI_WR_BUF_ENABLE, &priv->regs->cfg[1]);
+	while (len >= MTK_QSPI_WRBUF_SIZE) {
+		ret = mtk_qspi_write_buffer(priv, addr, buf);
+		if (ret < 0)
+			return ret;
+
+		len -= MTK_QSPI_WRBUF_SIZE;
+		addr += MTK_QSPI_WRBUF_SIZE;
+		buf += MTK_QSPI_WRBUF_SIZE;
+	}
+	/* disable pre-fetch buffer for page program */
+	writel(MTK_QSPI_WR_BUF_DISABLE, &priv->regs->cfg[1]);
+
+	if (len)
+		return mtk_qspi_write_single_byte(priv, addr, len, buf);
+
+	return 0;
+}
+
+static int mtk_qspi_claim_bus(struct udevice *dev)
+{
+	/* nothing to do */
+	return 0;
+}
+
+static int mtk_qspi_release_bus(struct udevice *dev)
+{
+	/* nothing to do */
+	return 0;
+}
+
+static int mtk_qspi_transfer(struct mtk_qspi_priv *priv, unsigned int bitlen,
+			     const void *dout, void *din, unsigned long flags)
+{
+	u32 bytes = DIV_ROUND_UP(bitlen, 8);
+	u32 addr;
+
+	if (!bytes)
+		return -ERR_INVAL;
+
+	if (dout) {
+		if (flags & SPI_XFER_BEGIN) {
+			/* parse op code and potential paras first */
+			priv->op = *(u8 *)dout;
+			if (bytes > 1)
+				memcpy(priv->tx, (u8 *)dout + 1,
+				       bytes <= 4 ? bytes - 1 : 3);
+			priv->txlen = bytes - 1;
+		}
+
+		if (flags == SPI_XFER_ONCE) {
+			/* operations without receiving or sending data.
+			 * for example: erase, write flash register or write
+			 * enable...
+			 */
+			priv->rx = NULL;
+			priv->rxlen = 0;
+			return mtk_qspi_tx_rx(priv);
+		}
+
+		if (flags & SPI_XFER_END) {
+			/* here, dout should be data to be written.
+			 * and priv->tx should be filled 3Bytes address.
+			 */
+			addr = priv->tx[0] << 16 | priv->tx[1] << 8 |
+			       priv->tx[2];
+			return mtk_qspi_write(priv, addr, (u8 *)dout, bytes);
+		}
+	}
+
+	if (din) {
+		if (priv->txlen >= 3) {
+			/* if run to here, priv->tx[] should be the address
+			 * where read data from,
+			 * and, din is the buf to receive data.
+			 */
+			addr = priv->tx[0] << 16 | priv->tx[1] << 8 |
+			       priv->tx[2];
+			return mtk_qspi_read(priv, addr, (u8 *)din, bytes);
+		}
+
+		/* should be reading flash's register */
+		priv->rx = (u8 *)din;
+		priv->rxlen = bytes;
+		return mtk_qspi_tx_rx(priv);
+	}
+
+	return 0;
+}
+
+static int mtk_qspi_xfer(struct udevice *dev, unsigned int bitlen,
+			 const void *dout, void *din, unsigned long flags)
+{
+	struct udevice *bus = dev->parent;
+	struct mtk_qspi_priv *priv = dev_get_priv(bus);
+
+	return  mtk_qspi_transfer(priv, bitlen, dout, din, flags);
+}
+
+static int mtk_qspi_set_speed(struct udevice *bus, uint speed)
+{
+	/* nothing to do */
+	return 0;
+}
+
+static int mtk_qspi_set_mode(struct udevice *bus, uint mode)
+{
+	/* nothing to do */
+	return 0;
+}
+
+static int mtk_qspi_ofdata_to_platdata(struct udevice *bus)
+{
+	struct resource res_reg, res_mem;
+	struct mtk_qspi_platdata *plat = bus->platdata;
+	int ret;
+
+	ret = dev_read_resource_byname(bus, "reg_base", &res_reg);
+	if (ret) {
+		debug("can't get reg_base resource(ret = %d)\n", ret);
+		return -ENOMEM;
+	}
+
+	ret = dev_read_resource_byname(bus, "mem_base", &res_mem);
+	if (ret) {
+		debug("can't get map_base resource(ret = %d)\n", ret);
+		return -ENOMEM;
+	}
+
+	plat->mem_base = res_mem.start;
+	plat->reg_base = res_reg.start;
+
+	return 0;
+}
+
+static int mtk_qspi_probe(struct udevice *bus)
+{
+	struct mtk_qspi_platdata *plat = dev_get_platdata(bus);
+	struct mtk_qspi_priv *priv = dev_get_priv(bus);
+
+	priv->regs = (struct mtk_qspi_regs *)plat->reg_base;
+	priv->mem_base = (unsigned long *)plat->mem_base;
+
+	writel(MTK_QSPI_COMMAND_ENABLE, &priv->regs->wrprot);
+
+	return 0;
+}
+
+static const struct dm_spi_ops mtk_qspi_ops = {
+	.claim_bus      = mtk_qspi_claim_bus,
+	.release_bus    = mtk_qspi_release_bus,
+	.xfer           = mtk_qspi_xfer,
+	.set_speed      = mtk_qspi_set_speed,
+	.set_mode       = mtk_qspi_set_mode,
+};
+
+static const struct udevice_id mtk_qspi_ids[] = {
+	{ .compatible = "mediatek,mt7629-qspi" },
+	{ }
+};
+
+U_BOOT_DRIVER(mtk_qspi) = {
+	.name     = "mtk_qspi",
+	.id       = UCLASS_SPI,
+	.of_match = mtk_qspi_ids,
+	.ops      = &mtk_qspi_ops,
+	.ofdata_to_platdata       = mtk_qspi_ofdata_to_platdata,
+	.platdata_auto_alloc_size = sizeof(struct mtk_qspi_platdata),
+	.priv_auto_alloc_size     = sizeof(struct mtk_qspi_priv),
+	.probe    = mtk_qspi_probe,
+};
diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c
index 0dccc38..b263690 100644
--- a/drivers/spi/mxc_spi.c
+++ b/drivers/spi/mxc_spi.c
@@ -400,10 +400,6 @@
 	return mxc_spi_xfer_internal(mxcs, bitlen, dout, din, flags);
 }
 
-void spi_init(void)
-{
-}
-
 /*
  * Some SPI devices require active chip-select over multiple
  * transactions, we achieve this using a GPIO. Still, the SPI
diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c
index 006fe82..5065e40 100644
--- a/drivers/spi/mxs_spi.c
+++ b/drivers/spi/mxs_spi.c
@@ -39,10 +39,6 @@
 	return container_of(slave, struct mxs_spi_slave, slave);
 }
 
-void spi_init(void)
-{
-}
-
 int spi_cs_is_valid(unsigned int bus, unsigned int cs)
 {
 	/* MXS SPI: 4 ports and 3 chip selects maximum */
diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c
index ecf54bb..c7fcf05 100644
--- a/drivers/spi/omap3_spi.c
+++ b/drivers/spi/omap3_spi.c
@@ -461,11 +461,6 @@
 	return container_of(slave, struct omap3_spi_priv, slave);
 }
 
-void spi_init(void)
-{
-	/* do nothing */
-}
-
 void spi_free_slave(struct spi_slave *slave)
 {
 	struct omap3_spi_priv *priv = to_omap3_spi(slave);
diff --git a/drivers/spi/pl022_spi.c b/drivers/spi/pl022_spi.c
index 86b71d2..32bb8c8 100644
--- a/drivers/spi/pl022_spi.c
+++ b/drivers/spi/pl022_spi.c
@@ -9,16 +9,11 @@
  * Driver for ARM PL022 SPI Controller.
  */
 
-#include <asm/io.h>
 #include <clk.h>
 #include <common.h>
 #include <dm.h>
-#include <dm/platform_data/pl022_spi.h>
-#include <fdtdec.h>
-#include <linux/bitops.h>
-#include <linux/bug.h>
+#include <dm/platform_data/spi_pl022.h>
 #include <linux/io.h>
-#include <linux/kernel.h>
 #include <spi.h>
 
 #define SSP_CR0		0x000
@@ -72,11 +67,7 @@
 
 struct pl022_spi_slave {
 	void *base;
-#if !CONFIG_IS_ENABLED(OF_PLATDATA)
-	struct clk clk;
-#else
 	unsigned int freq;
-#endif
 };
 
 /*
@@ -96,30 +87,13 @@
 	return 0;
 }
 
-#if !CONFIG_IS_ENABLED(OF_PLATDATA)
-static int pl022_spi_ofdata_to_platdata(struct udevice *bus)
-{
-	struct pl022_spi_pdata *plat = bus->platdata;
-	const void *fdt = gd->fdt_blob;
-	int node = dev_of_offset(bus);
-
-	plat->addr = fdtdec_get_addr_size(fdt, node, "reg", &plat->size);
-
-	return clk_get_by_index(bus, 0, &plat->clk);
-}
-#endif
-
 static int pl022_spi_probe(struct udevice *bus)
 {
 	struct pl022_spi_pdata *plat = dev_get_platdata(bus);
 	struct pl022_spi_slave *ps = dev_get_priv(bus);
 
 	ps->base = ioremap(plat->addr, plat->size);
-#if !CONFIG_IS_ENABLED(OF_PLATDATA)
-	ps->clk = plat->clk;
-#else
 	ps->freq = plat->freq;
-#endif
 
 	/* Check the PL022 version */
 	if (!pl022_is_supported(ps))
@@ -240,11 +214,7 @@
 	u16 scr = SSP_SCR_MIN, cr0 = 0, cpsr = SSP_CPSR_MIN, best_scr = scr,
 	    best_cpsr = cpsr;
 	u32 min, max, best_freq = 0, tmp;
-#if !CONFIG_IS_ENABLED(OF_PLATDATA)
-	u32 rate = clk_get_rate(&ps->clk);
-#else
 	u32 rate = ps->freq;
-#endif
 	bool found = false;
 
 	max = spi_rate(rate, SSP_CPSR_MIN, SSP_SCR_MIN);
@@ -316,6 +286,25 @@
 };
 
 #if !CONFIG_IS_ENABLED(OF_PLATDATA)
+static int pl022_spi_ofdata_to_platdata(struct udevice *bus)
+{
+	struct pl022_spi_pdata *plat = bus->platdata;
+	const void *fdt = gd->fdt_blob;
+	int node = dev_of_offset(bus);
+	struct clk clkdev;
+	int ret;
+
+	plat->addr = fdtdec_get_addr_size(fdt, node, "reg", &plat->size);
+
+	ret = clk_get_by_index(bus, 0, &clkdev);
+	if (ret)
+		return ret;
+
+	plat->freq = clk_get_rate(&clkdev);
+
+	return 0;
+}
+
 static const struct udevice_id pl022_spi_ids[] = {
 	{ .compatible = "arm,pl022-spi" },
 	{ }
@@ -327,11 +316,9 @@
 	.id     = UCLASS_SPI,
 #if !CONFIG_IS_ENABLED(OF_PLATDATA)
 	.of_match = pl022_spi_ids,
-#endif
-	.ops    = &pl022_spi_ops,
-#if !CONFIG_IS_ENABLED(OF_PLATDATA)
 	.ofdata_to_platdata = pl022_spi_ofdata_to_platdata,
 #endif
+	.ops    = &pl022_spi_ops,
 	.platdata_auto_alloc_size = sizeof(struct pl022_spi_pdata),
 	.priv_auto_alloc_size = sizeof(struct pl022_spi_slave),
 	.probe  = pl022_spi_probe,
diff --git a/drivers/spi/sh_qspi.c b/drivers/spi/sh_qspi.c
index 64dfd74..5ae203d 100644
--- a/drivers/spi/sh_qspi.c
+++ b/drivers/spi/sh_qspi.c
@@ -247,11 +247,6 @@
 	sh_qspi_cs_deactivate(ss);
 }
 
-void spi_init(void)
-{
-	/* nothing to do */
-}
-
 struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
 		unsigned int max_hz, unsigned int mode)
 {
diff --git a/drivers/spi/sh_spi.c b/drivers/spi/sh_spi.c
index bc2bd63..c58fd0e 100644
--- a/drivers/spi/sh_spi.c
+++ b/drivers/spi/sh_spi.c
@@ -66,10 +66,6 @@
 	return 0;
 }
 
-void spi_init(void)
-{
-}
-
 static void sh_spi_set_cs(struct sh_spi *ss, unsigned int cs)
 {
 	unsigned long val = 0;
diff --git a/drivers/spi/soft_spi_legacy.c b/drivers/spi/soft_spi_legacy.c
index 0aac0c0..cc5ab5f 100644
--- a/drivers/spi/soft_spi_legacy.c
+++ b/drivers/spi/soft_spi_legacy.c
@@ -36,13 +36,6 @@
 /*                         Public Functions                            */
 /*=====================================================================*/
 
-/*-----------------------------------------------------------------------
- * Initialization
- */
-void spi_init (void)
-{
-}
-
 struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
 		unsigned int max_hz, unsigned int mode)
 {
diff --git a/examples/standalone/atmel_df_pow2.c b/examples/standalone/atmel_df_pow2.c
index 2e14aba..b7bd243 100644
--- a/examples/standalone/atmel_df_pow2.c
+++ b/examples/standalone/atmel_df_pow2.c
@@ -126,8 +126,6 @@
 		return 1;
 	}
 
-	spi_init();
-
 	while (1) {
 		struct spi_slave *slave;
 		char *line, *p;
diff --git a/include/_exports.h b/include/_exports.h
index 5416041..c15050e 100644
--- a/include/_exports.h
+++ b/include/_exports.h
@@ -50,11 +50,9 @@
 #endif
 
 #if !defined(CONFIG_CMD_SPI) || defined(CONFIG_DM_SPI)
-	EXPORT_FUNC(dummy, void, spi_init, void)
 	EXPORT_FUNC(dummy, void, spi_setup_slave, void)
 	EXPORT_FUNC(dummy, void, spi_free_slave, void)
 #else
-	EXPORT_FUNC(spi_init, void, spi_init, void)
 	EXPORT_FUNC(spi_setup_slave, struct spi_slave *, spi_setup_slave,
 		    unsigned int, unsigned int, unsigned int, unsigned int)
 	EXPORT_FUNC(spi_free_slave, void, spi_free_slave, struct spi_slave *)
diff --git a/include/common.h b/include/common.h
index a8e879e..faf512e 100644
--- a/include/common.h
+++ b/include/common.h
@@ -287,13 +287,6 @@
 # define CONFIG_SYS_DEF_EEPROM_ADDR CONFIG_SYS_I2C_EEPROM_ADDR
 #endif
 
-#if defined(CONFIG_MPC8XX_SPI)
-extern void spi_init_f (void);
-extern void spi_init_r (void);
-extern ssize_t spi_read	 (uchar *, int, uchar *, int);
-extern ssize_t spi_write (uchar *, int, uchar *, int);
-#endif
-
 /* $(BOARD)/$(BOARD).c */
 int board_early_init_f (void);
 int board_fix_fdt (void *rw_fdt_blob); /* manipulate the U-Boot fdt before its relocation */
diff --git a/include/configs/M52277EVB.h b/include/configs/M52277EVB.h
index 11cb395..83d7745 100644
--- a/include/configs/M52277EVB.h
+++ b/include/configs/M52277EVB.h
@@ -102,7 +102,6 @@
 
 /* DSPI and Serial Flash */
 #define CONFIG_CF_DSPI
-#define CONFIG_HARD_SPI
 #define CONFIG_SYS_SBFHDR_SIZE		0x7
 #ifdef CONFIG_CMD_SPI
 #	define CONFIG_SYS_DSPI_CS2
diff --git a/include/configs/M54418TWR.h b/include/configs/M54418TWR.h
index f08896e..4b8ef38 100644
--- a/include/configs/M54418TWR.h
+++ b/include/configs/M54418TWR.h
@@ -151,7 +151,6 @@
 /* DSPI and Serial Flash */
 #define CONFIG_CF_DSPI
 #define CONFIG_SERIAL_FLASH
-#define CONFIG_HARD_SPI
 #define CONFIG_SYS_SBFHDR_SIZE		0x7
 #ifdef CONFIG_CMD_SPI
 
diff --git a/include/configs/M54451EVB.h b/include/configs/M54451EVB.h
index 16becbd..87cdbae 100644
--- a/include/configs/M54451EVB.h
+++ b/include/configs/M54451EVB.h
@@ -116,7 +116,6 @@
 /* DSPI and Serial Flash */
 #define CONFIG_CF_DSPI
 #define CONFIG_SERIAL_FLASH
-#define CONFIG_HARD_SPI
 #define CONFIG_SYS_SBFHDR_SIZE		0x7
 #ifdef CONFIG_CMD_SPI
 
diff --git a/include/configs/M54455EVB.h b/include/configs/M54455EVB.h
index 99b60d5..d41b7c4 100644
--- a/include/configs/M54455EVB.h
+++ b/include/configs/M54455EVB.h
@@ -142,7 +142,6 @@
 
 /* DSPI and Serial Flash */
 #define CONFIG_CF_DSPI
-#define CONFIG_HARD_SPI
 #define CONFIG_SYS_SBFHDR_SIZE		0x13
 #ifdef CONFIG_CMD_SPI
 
diff --git a/include/configs/MPC8536DS.h b/include/configs/MPC8536DS.h
index 524a10f..86a1233 100644
--- a/include/configs/MPC8536DS.h
+++ b/include/configs/MPC8536DS.h
@@ -370,11 +370,6 @@
 #define CONFIG_SYS_I2C_EEPROM_ADDR_LEN	1
 #define CONFIG_SYS_EEPROM_BUS_NUM	1
 
-/*
- * eSPI - Enhanced SPI
- */
-#define CONFIG_HARD_SPI
-
 #if defined(CONFIG_SPI_FLASH)
 #define CONFIG_SF_DEFAULT_SPEED	10000000
 #define CONFIG_SF_DEFAULT_MODE	0
diff --git a/include/configs/P1022DS.h b/include/configs/P1022DS.h
index c9ed70c..eeb19a9 100644
--- a/include/configs/P1022DS.h
+++ b/include/configs/P1022DS.h
@@ -386,12 +386,6 @@
 #define CONFIG_SYS_I2C_EEPROM_ADDR_LEN	1
 #define CONFIG_SYS_EEPROM_BUS_NUM	1
 
-/*
- * eSPI - Enhanced SPI
- */
-
-#define CONFIG_HARD_SPI
-
 #define CONFIG_SF_DEFAULT_SPEED		10000000
 #define CONFIG_SF_DEFAULT_MODE		0
 
diff --git a/include/configs/UCP1020.h b/include/configs/UCP1020.h
index 423ecd7..1bbe9d9 100644
--- a/include/configs/UCP1020.h
+++ b/include/configs/UCP1020.h
@@ -287,11 +287,6 @@
 #define CONFIG_SYS_I2C_NCT72_ADDR	0x4C
 #define CONFIG_SYS_I2C_IDT6V49205B	0x69
 
-/*
- * eSPI - Enhanced SPI
- */
-#define CONFIG_HARD_SPI
-
 #define CONFIG_SF_DEFAULT_SPEED		10000000
 #define CONFIG_SF_DEFAULT_MODE		SPI_MODE_0
 
diff --git a/include/configs/controlcenterd.h b/include/configs/controlcenterd.h
index 4adcd95..1908d35 100644
--- a/include/configs/controlcenterd.h
+++ b/include/configs/controlcenterd.h
@@ -182,10 +182,6 @@
 #define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 2
 
 #ifndef CONFIG_TRAILBLAZER
-/*
- * eSPI - Enhanced SPI
- */
-#define CONFIG_HARD_SPI
 
 #define CONFIG_SF_DEFAULT_SPEED		10000000
 #define CONFIG_SF_DEFAULT_MODE		0
diff --git a/include/configs/ids8313.h b/include/configs/ids8313.h
index 28124dd..7e4c497 100644
--- a/include/configs/ids8313.h
+++ b/include/configs/ids8313.h
@@ -159,7 +159,6 @@
  */
 #define CONFIG_TSEC1
 #define CONFIG_TSEC2
-#define CONFIG_HARD_SPI
 
 /*
  * NOR FLASH setup
@@ -274,15 +273,6 @@
 #define CONFIG_SYS_I2C_RTC_ADDR	0x51
 
 /*
- * SPI setup
- */
-#ifdef CONFIG_HARD_SPI
-#define CONFIG_SYS_GPIO1_PRELIM
-#define CONFIG_SYS_GPIO1_DIR		0x00000001
-#define CONFIG_SYS_GPIO1_DAT		0x00000001
-#endif
-
-/*
  * Ethernet setup
  */
 #ifdef CONFIG_TSEC1
diff --git a/include/configs/mx31pdk.h b/include/configs/mx31pdk.h
index 7d84d16..4765764 100644
--- a/include/configs/mx31pdk.h
+++ b/include/configs/mx31pdk.h
@@ -43,7 +43,6 @@
 #define CONFIG_MXC_UART
 #define CONFIG_MXC_UART_BASE	UART1_BASE
 
-#define CONFIG_HARD_SPI
 #define CONFIG_DEFAULT_SPI_BUS	1
 #define CONFIG_DEFAULT_SPI_MODE	(SPI_MODE_0 | SPI_CS_HIGH)
 
diff --git a/include/configs/mxs.h b/include/configs/mxs.h
index 9e59e7a..4bb3621 100644
--- a/include/configs/mxs.h
+++ b/include/configs/mxs.h
@@ -143,7 +143,6 @@
 
 /* SPI */
 #ifdef CONFIG_CMD_SPI
-#define CONFIG_HARD_SPI
 #define CONFIG_SPI_HALF_DUPLEX
 #endif
 
diff --git a/include/configs/p1_p2_rdb_pc.h b/include/configs/p1_p2_rdb_pc.h
index 9465fb4..459ecf3 100644
--- a/include/configs/p1_p2_rdb_pc.h
+++ b/include/configs/p1_p2_rdb_pc.h
@@ -576,11 +576,6 @@
 #define CONFIG_SYS_EEPROM_PAGE_WRITE_BITS 3
 #define CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS 5
 
-/*
- * eSPI - Enhanced SPI
- */
-#define CONFIG_HARD_SPI
-
 #if defined(CONFIG_SPI_FLASH)
 #define CONFIG_SF_DEFAULT_SPEED	10000000
 #define CONFIG_SF_DEFAULT_MODE	0
diff --git a/include/configs/p1_twr.h b/include/configs/p1_twr.h
index d018c22..4f48370 100644
--- a/include/configs/p1_twr.h
+++ b/include/configs/p1_twr.h
@@ -214,11 +214,6 @@
 #define CONFIG_SYS_EEPROM_PAGE_WRITE_BITS 3
 #define CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS 5
 
-/*
- * eSPI - Enhanced SPI
- */
-#define CONFIG_HARD_SPI
-
 #if defined(CONFIG_PCI)
 /*
  * General PCI
diff --git a/include/configs/stmark2.h b/include/configs/stmark2.h
index c408db8..33ddc67 100644
--- a/include/configs/stmark2.h
+++ b/include/configs/stmark2.h
@@ -66,7 +66,6 @@
 #define CONFIG_CF_DSPI
 #define CONFIG_SF_DEFAULT_SPEED		50000000
 #define CONFIG_SERIAL_FLASH
-#define CONFIG_HARD_SPI
 #define CONFIG_ENV_SPI_BUS		0
 #define CONFIG_ENV_SPI_CS		1
 
diff --git a/include/configs/ts4800.h b/include/configs/ts4800.h
index 956f779..4e274bd 100644
--- a/include/configs/ts4800.h
+++ b/include/configs/ts4800.h
@@ -43,11 +43,6 @@
 #define CONFIG_MXC_UART_BASE	UART1_BASE
 
 /*
- * SPI Configs
- * */
-#define CONFIG_HARD_SPI /* puts SPI: ready */
-
-/*
  * MMC Configs
  * */
 #define CONFIG_SYS_FSL_ESDHC_ADDR	MMC_SDHC1_BASE_ADDR
diff --git a/include/dm/platform_data/pl022_spi.h b/include/dm/platform_data/spi_pl022.h
similarity index 64%
rename from include/dm/platform_data/pl022_spi.h
rename to include/dm/platform_data/spi_pl022.h
index 77fe6da..63a58ee 100644
--- a/include/dm/platform_data/pl022_spi.h
+++ b/include/dm/platform_data/spi_pl022.h
@@ -7,22 +7,15 @@
  * in ofdata_to_platdata.
  */
 
-#ifndef __PL022_SPI_H__
-#define __PL022_SPI_H__
+#ifndef __spi_pl022_h
+#define __spi_pl022_h
 
-#if !CONFIG_IS_ENABLED(OF_PLATDATA)
-#include <clk.h>
-#endif
 #include <fdtdec.h>
 
 struct pl022_spi_pdata {
 	fdt_addr_t addr;
 	fdt_size_t size;
-#if !CONFIG_IS_ENABLED(OF_PLATDATA)
-	struct clk clk;
-#else
 	unsigned int freq;
-#endif
 };
 
-#endif
+#endif /* __spi_pl022_h */
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 68e5915..cd1f557 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -366,6 +366,8 @@
 	return !list_empty(&mtd->partitions);
 }
 
+bool mtd_partitions_used(struct mtd_info *master);
+
 int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
 		      struct mtd_oob_region *oobecc);
 int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte,
@@ -562,8 +564,23 @@
 /* drivers/mtd/mtdcore.h */
 int add_mtd_device(struct mtd_info *mtd);
 int del_mtd_device(struct mtd_info *mtd);
+
+#ifdef CONFIG_MTD_PARTITIONS
 int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
 int del_mtd_partitions(struct mtd_info *);
+#else
+static inline int add_mtd_partitions(struct mtd_info *mtd,
+				     const struct mtd_partition *parts,
+				     int nparts)
+{
+	return 0;
+}
+
+static inline int del_mtd_partitions(struct mtd_info *mtd)
+{
+	return 0;
+}
+#endif
 
 struct mtd_info *__mtd_next_device(int i);
 #define mtd_for_each_device(mtd)			\
@@ -581,6 +598,7 @@
 void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset,
 			  const uint64_t length, uint64_t *len_incl_bad,
 			  int *truncated);
+bool mtd_dev_list_updated(void);
 
 /* drivers/mtd/mtd_uboot.c */
 int mtd_search_alternate_name(const char *mtdname, char *altname,
diff --git a/include/regmap.h b/include/regmap.h
index b2b733f..a3afb72 100644
--- a/include/regmap.h
+++ b/include/regmap.h
@@ -240,6 +240,44 @@
 	regmap_range_get(map, 0, type, member, valp)
 
 /**
+ * regmap_read_poll_timeout - Poll until a condition is met or a timeout occurs
+ *
+ * @map:	Regmap to read from
+ * @addr:	Offset to poll
+ * @val:	Unsigned integer variable to read the value into
+ * @cond:	Break condition (usually involving @val)
+ * @sleep_us:	Maximum time to sleep between reads in us (0 tight-loops).
+ * @timeout_ms:	Timeout in ms, 0 means never timeout
+ *
+ * Returns 0 on success and -ETIMEDOUT upon a timeout or the regmap_read
+ * error return value in case of a error read. In the two former cases,
+ * the last read value at @addr is stored in @val. Must not be called
+ * from atomic context if sleep_us or timeout_us are used.
+ *
+ * This is modelled after the regmap_read_poll_timeout macros in linux but
+ * with millisecond timeout.
+ */
+#define regmap_read_poll_timeout(map, addr, val, cond, sleep_us, timeout_ms) \
+({ \
+	unsigned long __start = get_timer(0); \
+	int __ret; \
+	for (;;) { \
+		__ret = regmap_read((map), (addr), &(val)); \
+		if (__ret) \
+			break; \
+		if (cond) \
+			break; \
+		if ((timeout_ms) && get_timer(__start) > (timeout_ms)) { \
+			__ret = regmap_read((map), (addr), &(val)); \
+			break; \
+		} \
+		if ((sleep_us)) \
+			udelay((sleep_us)); \
+	} \
+	__ret ?: ((cond) ? 0 : -ETIMEDOUT); \
+})
+
+/**
  * regmap_update_bits() - Perform a read/modify/write using a mask
  *
  * @map:	The map returned by regmap_init_mem*()
diff --git a/include/spi.h b/include/spi.h
index 938627b..92427e5 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -118,13 +118,6 @@
 };
 
 /**
- * Initialization, must be called once on start up.
- *
- * TODO: I don't think we really need this.
- */
-void spi_init(void);
-
-/**
  * spi_do_alloc_slave - Allocate a new SPI slave (internal)
  *
  * Allocate and zero all fields in the spi slave, and set the bus/chip
diff --git a/scripts/config_whitelist.txt b/scripts/config_whitelist.txt
index bd14fe2..b8addea 100644
--- a/scripts/config_whitelist.txt
+++ b/scripts/config_whitelist.txt
@@ -750,7 +750,6 @@
 CONFIG_G_DNL_UMS_VENDOR_NUM
 CONFIG_H264_FREQ
 CONFIG_H8300
-CONFIG_HARD_SPI
 CONFIG_HAS_ETH0
 CONFIG_HAS_ETH1
 CONFIG_HAS_ETH2
diff --git a/test/dm/regmap.c b/test/dm/regmap.c
index a8d7e68..9a70c15 100644
--- a/test/dm/regmap.c
+++ b/test/dm/regmap.c
@@ -144,3 +144,29 @@
 }
 
 DM_TEST(dm_test_regmap_getset, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Read polling test */
+static int dm_test_regmap_poll(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	struct regmap *map;
+	uint reg;
+	unsigned long start;
+
+	ut_assertok(uclass_get_device(UCLASS_SYSCON, 0, &dev));
+	map = syscon_get_regmap(dev);
+	ut_assertok_ptr(map);
+
+	start = get_timer(0);
+
+	ut_asserteq(-ETIMEDOUT,
+		    regmap_read_poll_timeout(map, 0, reg,
+					     (reg == 0xcacafafa),
+					     1, 5 * CONFIG_SYS_HZ));
+
+	ut_assert(get_timer(start) > (5 * CONFIG_SYS_HZ));
+
+	return 0;
+}
+
+DM_TEST(dm_test_regmap_poll, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);