diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/mtd/mtd_uboot.c | 185 | ||||
| -rw-r--r-- | drivers/mtd/mtdcore.c | 23 | ||||
| -rw-r--r-- | drivers/mtd/mtdpart.c | 12 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/pxa3xx_nand.c | 143 | ||||
| -rw-r--r-- | drivers/mtd/spi/sf_mtd.c | 48 | ||||
| -rw-r--r-- | drivers/mtd/spi/sf_probe.c | 9 | ||||
| -rw-r--r-- | drivers/net/e1000_spi.c | 3 | ||||
| -rw-r--r-- | drivers/pci/pci_rom.c | 1 | ||||
| -rw-r--r-- | drivers/spi/Kconfig | 27 | ||||
| -rw-r--r-- | drivers/spi/Makefile | 2 | ||||
| -rw-r--r-- | drivers/spi/atmel_spi.c | 5 | ||||
| -rw-r--r-- | drivers/spi/davinci_spi.c | 5 | ||||
| -rw-r--r-- | drivers/spi/fsl_dspi.c | 5 | ||||
| -rw-r--r-- | drivers/spi/fsl_espi.c | 5 | ||||
| -rw-r--r-- | drivers/spi/lpc32xx_ssp.c | 9 | ||||
| -rw-r--r-- | drivers/spi/meson_spifc.c | 320 | ||||
| -rw-r--r-- | drivers/spi/mtk_qspi.c | 359 | ||||
| -rw-r--r-- | drivers/spi/mxc_spi.c | 4 | ||||
| -rw-r--r-- | drivers/spi/mxs_spi.c | 4 | ||||
| -rw-r--r-- | drivers/spi/omap3_spi.c | 5 | ||||
| -rw-r--r-- | drivers/spi/pl022_spi.c | 55 | ||||
| -rw-r--r-- | drivers/spi/sh_qspi.c | 5 | ||||
| -rw-r--r-- | drivers/spi/sh_spi.c | 4 | ||||
| -rw-r--r-- | drivers/spi/soft_spi_legacy.c | 7 | ||||
| -rw-r--r-- | drivers/video/video-uclass.c | 4 | ||||
| -rw-r--r-- | drivers/video/video_bmp.c | 3 | 
26 files changed, 1067 insertions, 185 deletions
| diff --git a/drivers/mtd/mtd_uboot.c b/drivers/mtd/mtd_uboot.c index 5ca560c9687..d638f700d04 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 @@ int mtd_search_alternate_name(const char *mtdname, char *altname,  	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 @@ static void mtd_probe_uclass_mtd_devs(void) { }  #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 @@ static const char *get_mtdparts(void)  	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 @@ int mtd_probe_devices(void)  	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 @@ int mtd_probe_devices(void)  			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 @@ int mtd_probe_devices(void)  		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 fb6c779abbf..cb7ca38d074 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -87,14 +87,17 @@ struct idr_layer {  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 @@ int idr_alloc(struct idr *idp, void *ptr, int start, int end, gfp_t gfp_mask)  		if (idl->used == 0) {  			idl->used = 1;  			idl->ptr = ptr; +			idp->updated = true;  			return i;  		}  		i++; @@ -155,6 +159,16 @@ struct mtd_info *__mtd_next_device(int i)  }  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 @@ int del_mtd_device(struct mtd_info *mtd)  	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 4d2ac8107f0..fd8d8e5ea72 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -63,6 +63,18 @@ char *kstrdup(const char *s, gfp_t gfp)  #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 4c783f1e1e4..4d2712df4c7 100644 --- a/drivers/mtd/nand/raw/pxa3xx_nand.c +++ b/drivers/mtd/nand/raw/pxa3xx_nand.c @@ -195,6 +195,7 @@ struct pxa3xx_nand_info {  	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_bch4bit = {  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 disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)  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 drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)  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 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)  		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 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)  	}  	/* 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 @@ static void prepare_start_command(struct pxa3xx_nand_info *info, int command)  	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 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,  		 * 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 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,  {  	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 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,  	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 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,  	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 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,  		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 @@ static int alloc_nand_resource(struct pxa3xx_nand_info *info)  		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 58d7e443990..68c36002bee 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 @@ static int spi_flash_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)  	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 @@ static int spi_flash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,  	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 @@ static int spi_flash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,  	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 @@ static int spi_flash_mtd_number(void)  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 @@ int spi_flash_mtd_register(struct spi_flash *flash)  	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 5a2e932de8f..00f8558e701 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -144,6 +144,14 @@ static int spi_flash_std_probe(struct udevice *dev)  	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 @@ U_BOOT_DRIVER(spi_flash_std) = {  	.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 b38f4df9f31..aecd290d729 100644 --- a/drivers/net/e1000_spi.c +++ b/drivers/net/e1000_spi.c @@ -77,9 +77,6 @@ static inline struct e1000_hw *e1000_hw_from_spi(struct spi_slave *spi)  	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/pci/pci_rom.c b/drivers/pci/pci_rom.c index eaacd4066e8..7d9b75c2c45 100644 --- a/drivers/pci/pci_rom.c +++ b/drivers/pci/pci_rom.c @@ -331,6 +331,7 @@ int vbe_setup_video_priv(struct vesa_mode_info *vesa,  		return log_msg_ret("No x resolution", -ENXIO);  	uc_priv->xsize = vesa->x_resolution;  	uc_priv->ysize = vesa->y_resolution; +	uc_priv->line_length = vesa->bytes_per_scanline;  	switch (vesa->bits_per_pixel) {  	case 32:  	case 24: diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 516188ea88e..a7bb5b35c29 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -116,6 +116,20 @@ config ICH_SPI  	  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 @@ config MT7621_SPI  	  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 @@ config LPC32XX_SSP  	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 7242ea7e404..392a9257957 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -31,8 +31,10 @@ obj-$(CONFIG_FSL_QSPI) += fsl_qspi.o  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 1db8bbef2bb..cf4de9ee1aa 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -34,11 +34,6 @@ static int spi_has_wdrbt(struct atmel_spi_slave *slave)  	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 07fa5e3b8a1..4d2c106440b 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -388,11 +388,6 @@ void spi_cs_deactivate(struct spi_slave *slave)  	/* 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 f7ed8fbe08b..764c94215e7 100644 --- a/drivers/spi/fsl_dspi.c +++ b/drivers/spi/fsl_dspi.c @@ -390,11 +390,6 @@ static int fsl_dspi_cfg_speed(struct fsl_dspi_priv *priv, uint speed)  	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 e9941593f5f..7444ae1a068 100644 --- a/drivers/spi/fsl_espi.c +++ b/drivers/spi/fsl_espi.c @@ -118,11 +118,6 @@ void spi_free_slave(struct spi_slave *slave)  	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 ce12eee6571..4b09366317a 100644 --- a/drivers/spi/lpc32xx_ssp.c +++ b/drivers/spi/lpc32xx_ssp.c @@ -47,15 +47,6 @@ static inline struct lpc32xx_spi_slave *to_lpc32xx_spi_slave(  	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 00000000000..3d551694cb0 --- /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 00000000000..b510733e92c --- /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 0dccc38b82d..b2636909ce6 100644 --- a/drivers/spi/mxc_spi.c +++ b/drivers/spi/mxc_spi.c @@ -400,10 +400,6 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,  	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 006fe8281c5..5065e407f82 100644 --- a/drivers/spi/mxs_spi.c +++ b/drivers/spi/mxs_spi.c @@ -39,10 +39,6 @@ static inline struct mxs_spi_slave *to_mxs_slave(struct spi_slave *slave)  	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 ecf54bb7148..c7fcf050a58 100644 --- a/drivers/spi/omap3_spi.c +++ b/drivers/spi/omap3_spi.c @@ -461,11 +461,6 @@ static inline struct omap3_spi_priv *to_omap3_spi(struct spi_slave *slave)  	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 86b71d2e21a..32bb8c8d212 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 @@ static int pl022_is_supported(struct pl022_spi_slave *ps)  	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 @@ static int pl022_spi_set_speed(struct udevice *bus, uint speed)  	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 @@ static const struct dm_spi_ops pl022_spi_ops = {  };  #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 @@ U_BOOT_DRIVER(pl022_spi) = {  	.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 64dfd748d67..5ae203d8d4f 100644 --- a/drivers/spi/sh_qspi.c +++ b/drivers/spi/sh_qspi.c @@ -247,11 +247,6 @@ void spi_cs_deactivate(struct spi_slave *slave)  	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 bc2bd638e6f..c58fd0ebc43 100644 --- a/drivers/spi/sh_spi.c +++ b/drivers/spi/sh_spi.c @@ -66,10 +66,6 @@ static int write_fifo_empty_wait(struct sh_spi *ss)  	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 0aac0c065da..cc5ab5f991d 100644 --- a/drivers/spi/soft_spi_legacy.c +++ b/drivers/spi/soft_spi_legacy.c @@ -36,13 +36,6 @@ static inline struct soft_spi_slave *to_soft_spi(struct spi_slave *slave)  /*                         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/drivers/video/video-uclass.c b/drivers/video/video-uclass.c index b6551b69d3a..f307cf243bd 100644 --- a/drivers/video/video-uclass.c +++ b/drivers/video/video-uclass.c @@ -226,7 +226,9 @@ static int video_post_probe(struct udevice *dev)  	/* Set up the line and display size */  	priv->fb = map_sysmem(plat->base, plat->size); -	priv->line_length = priv->xsize * VNBYTES(priv->bpix); +	if (!priv->line_length) +		priv->line_length = priv->xsize * VNBYTES(priv->bpix); +  	priv->fb_size = priv->line_length * priv->ysize;  	/* Set up colors  */ diff --git a/drivers/video/video_bmp.c b/drivers/video/video_bmp.c index 1377e190817..2898b0b55d7 100644 --- a/drivers/video/video_bmp.c +++ b/drivers/video/video_bmp.c @@ -7,6 +7,7 @@  #include <bmp_layout.h>  #include <dm.h>  #include <mapmem.h> +#include <splash.h>  #include <video.h>  #include <watchdog.h>  #include <asm/unaligned.h> @@ -140,8 +141,6 @@ __weak void fb_put_word(uchar **fb, uchar **from)  }  #endif /* CONFIG_BMP_16BPP */ -#define BMP_ALIGN_CENTER	0x7fff -  /**   * video_splash_align_axis() - Align a single coordinate   * | 
