diff options
Diffstat (limited to 'drivers')
57 files changed, 3903 insertions, 2596 deletions
| diff --git a/drivers/block/sym53c8xx.c b/drivers/block/sym53c8xx.c index c538e370e..6f1ac8572 100644 --- a/drivers/block/sym53c8xx.c +++ b/drivers/block/sym53c8xx.c @@ -248,7 +248,7 @@ void scsi_print_error (ccb * pccb)  /******************************************************************************   * sets-up the SCSI controller - * the base memory address is retrived via the pci_read_config_dword + * the base memory address is retrieved via the pci_read_config_dword   */  void scsi_low_level_init(int busdevfunc)  { diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index fca370ae0..de9e44e1e 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -12,6 +12,7 @@ LIB	= $(obj)libdfu.o  COBJS-$(CONFIG_DFU_FUNCTION) += dfu.o  COBJS-$(CONFIG_DFU_MMC) += dfu_mmc.o  COBJS-$(CONFIG_DFU_NAND) += dfu_nand.o +COBJS-$(CONFIG_DFU_RAM) += dfu_ram.o  SRCS    := $(COBJS-y:.o=.c)  OBJS	:= $(addprefix $(obj),$(COBJS-y)) diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index d73d51039..56b21c78a 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -41,6 +41,29 @@ static int dfu_find_alt_num(const char *s)  	return ++i;  } +int dfu_init_env_entities(char *interface, int dev) +{ +	const char *str_env; +	char *env_bkp; +	int ret; + +	str_env = getenv("dfu_alt_info"); +	if (!str_env) { +		error("\"dfu_alt_info\" env variable not defined!\n"); +		return -EINVAL; +	} + +	env_bkp = strdup(str_env); +	ret = dfu_config_entities(env_bkp, interface, dev); +	if (ret) { +		error("DFU entities configuration failed!\n"); +		return ret; +	} + +	free(env_bkp); +	return 0; +} +  static unsigned char *dfu_buf;  static unsigned long dfu_buf_size = CONFIG_SYS_DFU_DATA_BUF_SIZE; @@ -153,8 +176,8 @@ int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)  	/* we should be in buffer now (if not then size too large) */  	if ((dfu->i_buf + size) > dfu->i_buf_end) { -		printf("%s: Wrong size! [%d] [%d] - %d\n", -		       __func__, dfu->i_blk_seq_num, blk_seq_num, size); +		error("Buffer overflow! (0x%p + 0x%x > 0x%p)\n", dfu->i_buf, +		      size, dfu->i_buf_end);  		return -1;  	} @@ -325,6 +348,9 @@ static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt,  	} else if (strcmp(interface, "nand") == 0) {  		if (dfu_fill_entity_nand(dfu, s))  			return -1; +	} else if (strcmp(interface, "ram") == 0) { +		if (dfu_fill_entity_ram(dfu, s)) +			return -1;  	} else {  		printf("%s: Device %s not (yet) supported!\n",  		       __func__,  interface); @@ -374,14 +400,14 @@ int dfu_config_entities(char *env, char *interface, int num)  const char *dfu_get_dev_type(enum dfu_device_type t)  { -	const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND" }; +	const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM" };  	return dev_t[t];  }  const char *dfu_get_layout(enum dfu_layout l)  {  	const char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2", -					   "EXT3", "EXT4" }; +					   "EXT3", "EXT4", "RAM_ADDR" };  	return dfu_layout[l];  } diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c index 0871a770b..f94275869 100644 --- a/drivers/dfu/dfu_mmc.c +++ b/drivers/dfu/dfu_mmc.c @@ -13,16 +13,11 @@  #include <div64.h>  #include <dfu.h> -enum dfu_mmc_op { -	DFU_OP_READ = 1, -	DFU_OP_WRITE, -}; -  static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)  				dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE];  static long dfu_file_buf_len; -static int mmc_block_op(enum dfu_mmc_op op, struct dfu_entity *dfu, +static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,  			u64 offset, void *buf, long *len)  {  	char cmd_buf[DFU_CMD_BUF_SIZE]; @@ -65,7 +60,7 @@ static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len)  	return 0;  } -static int mmc_file_op(enum dfu_mmc_op op, struct dfu_entity *dfu, +static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu,  			void *buf, long *len)  {  	char cmd_buf[DFU_CMD_BUF_SIZE]; diff --git a/drivers/dfu/dfu_nand.c b/drivers/dfu/dfu_nand.c index 0ec12cff2..edbf5a97b 100644 --- a/drivers/dfu/dfu_nand.c +++ b/drivers/dfu/dfu_nand.c @@ -19,12 +19,7 @@  #include <jffs2/load_kernel.h>  #include <nand.h> -enum dfu_nand_op { -	DFU_OP_READ = 1, -	DFU_OP_WRITE, -}; - -static int nand_block_op(enum dfu_nand_op op, struct dfu_entity *dfu, +static int nand_block_op(enum dfu_op op, struct dfu_entity *dfu,  			u64 offset, void *buf, long *len)  {  	loff_t start, lim; diff --git a/drivers/dfu/dfu_ram.c b/drivers/dfu/dfu_ram.c new file mode 100644 index 000000000..335a8e1f2 --- /dev/null +++ b/drivers/dfu/dfu_ram.c @@ -0,0 +1,77 @@ +/* + * (C) Copyright 2013 + * Afzal Mohammed <afzal.mohd.ma@gmail.com> + * + * Reference: dfu_mmc.c + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski <l.majewski@samsung.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <malloc.h> +#include <errno.h> +#include <dfu.h> + +static int dfu_transfer_medium_ram(enum dfu_op op, struct dfu_entity *dfu, +				   u64 offset, void *buf, long *len) +{ +	if (dfu->layout != DFU_RAM_ADDR) { +		error("unsupported layout: %s\n", dfu_get_layout(dfu->layout)); +		return  -EINVAL; +	} + +	if (offset > dfu->data.ram.size) { +		error("request exceeds allowed area\n"); +		return -EINVAL; +	} + +	if (op == DFU_OP_WRITE) +		memcpy(dfu->data.ram.start + offset, buf, *len); +	else +		memcpy(buf, dfu->data.ram.start + offset, *len); + +	return 0; +} + +static int dfu_write_medium_ram(struct dfu_entity *dfu, u64 offset, +				void *buf, long *len) +{ +	return dfu_transfer_medium_ram(DFU_OP_WRITE, dfu, offset, buf, len); +} + +static int dfu_read_medium_ram(struct dfu_entity *dfu, u64 offset, +			       void *buf, long *len) +{ +	if (!*len) { +		*len = dfu->data.ram.size; +		return 0; +	} + +	return dfu_transfer_medium_ram(DFU_OP_READ, dfu, offset, buf, len); +} + +int dfu_fill_entity_ram(struct dfu_entity *dfu, char *s) +{ +	char *st; + +	dfu->dev_type = DFU_DEV_RAM; +	st = strsep(&s, " "); +	if (strcmp(st, "ram")) { +		error("unsupported device: %s\n", st); +		return -ENODEV; +	} + +	dfu->layout = DFU_RAM_ADDR; +	dfu->data.ram.start = (void *)simple_strtoul(s, &s, 16); +	s++; +	dfu->data.ram.size = simple_strtoul(s, &s, 16); + +	dfu->write_medium = dfu_write_medium_ram; +	dfu->read_medium = dfu_read_medium_ram; + +	dfu->inited = 0; + +	return 0; +} diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index bedf833f7..06280d1fa 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -34,6 +34,8 @@ COBJS-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o  COBJS-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o  ifdef CONFIG_SPL_BUILD  COBJS-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o +else +COBJS-$(CONFIG_GENERIC_MMC) += mmc_write.o  endif  COBJS	:= $(COBJS-y) diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index a82ee17a2..9a803a02d 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -41,12 +41,11 @@ static void dwmci_set_idma_desc(struct dwmci_idmac *idmac,  }  static void dwmci_prepare_data(struct dwmci_host *host, -		struct mmc_data *data) +		struct mmc_data *data, struct dwmci_idmac *cur_idmac)  {  	unsigned long ctrl;  	unsigned int i = 0, flags, cnt, blk_cnt;  	ulong data_start, data_end, start_addr; -	ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac, data->blocks);  	blk_cnt = data->blocks; @@ -73,7 +72,7 @@ static void dwmci_prepare_data(struct dwmci_host *host,  		dwmci_set_idma_desc(cur_idmac, flags, cnt,  				start_addr + (i * PAGE_SIZE)); -		if(blk_cnt < 8) +		if (blk_cnt <= 8)  			break;  		blk_cnt -= 8;  		cur_idmac++; @@ -111,6 +110,8 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,  		struct mmc_data *data)  {  	struct dwmci_host *host = (struct dwmci_host *)mmc->priv; +	ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac, +				 data ? DIV_ROUND_UP(data->blocks, 8) : 0);  	int flags = 0, i;  	unsigned int timeout = 100000;  	u32 retry = 10000; @@ -127,7 +128,7 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,  	dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL);  	if (data) -		dwmci_prepare_data(host, data); +		dwmci_prepare_data(host, data, cur_idmac);  	dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg); diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 55026759e..84dae4d8b 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -15,6 +15,7 @@  #include <malloc.h>  #include <linux/list.h>  #include <div64.h> +#include "mmc_private.h"  /* Set block count limit because of 16 bit register limit on some hardware*/  #ifndef CONFIG_SYS_MMC_MAX_BLK_COUNT @@ -52,14 +53,10 @@ int __board_mmc_getcd(struct mmc *mmc) {  int board_mmc_getcd(struct mmc *mmc)__attribute__((weak,  	alias("__board_mmc_getcd"))); -static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, -			struct mmc_data *data) +int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)  { -	struct mmc_data backup;  	int ret; -	memset(&backup, 0, sizeof(backup)); -  #ifdef CONFIG_MMC_TRACE  	int i;  	u8 *ptr; @@ -114,7 +111,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,  	return ret;  } -static int mmc_send_status(struct mmc *mmc, int timeout) +int mmc_send_status(struct mmc *mmc, int timeout)  {  	struct mmc_cmd cmd;  	int err, retries = 5; @@ -135,8 +132,10 @@ static int mmc_send_status(struct mmc *mmc, int timeout)  			     MMC_STATE_PRG)  				break;  			else if (cmd.response[0] & MMC_STATUS_MASK) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)  				printf("Status Error: 0x%08X\n",  					cmd.response[0]); +#endif  				return COMM_ERR;  			}  		} else if (--retries < 0) @@ -151,14 +150,16 @@ static int mmc_send_status(struct mmc *mmc, int timeout)  	printf("CURR STATE:%d\n", status);  #endif  	if (timeout <= 0) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)  		printf("Timeout waiting card ready\n"); +#endif  		return TIMEOUT;  	}  	return 0;  } -static int mmc_set_blocklen(struct mmc *mmc, int len) +int mmc_set_blocklen(struct mmc *mmc, int len)  {  	struct mmc_cmd cmd; @@ -181,179 +182,13 @@ struct mmc *find_mmc_device(int dev_num)  			return m;  	} +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)  	printf("MMC Device %d not found\n", dev_num); +#endif  	return NULL;  } -static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt) -{ -	struct mmc_cmd cmd; -	ulong end; -	int err, start_cmd, end_cmd; - -	if (mmc->high_capacity) -		end = start + blkcnt - 1; -	else { -		end = (start + blkcnt - 1) * mmc->write_bl_len; -		start *= mmc->write_bl_len; -	} - -	if (IS_SD(mmc)) { -		start_cmd = SD_CMD_ERASE_WR_BLK_START; -		end_cmd = SD_CMD_ERASE_WR_BLK_END; -	} else { -		start_cmd = MMC_CMD_ERASE_GROUP_START; -		end_cmd = MMC_CMD_ERASE_GROUP_END; -	} - -	cmd.cmdidx = start_cmd; -	cmd.cmdarg = start; -	cmd.resp_type = MMC_RSP_R1; - -	err = mmc_send_cmd(mmc, &cmd, NULL); -	if (err) -		goto err_out; - -	cmd.cmdidx = end_cmd; -	cmd.cmdarg = end; - -	err = mmc_send_cmd(mmc, &cmd, NULL); -	if (err) -		goto err_out; - -	cmd.cmdidx = MMC_CMD_ERASE; -	cmd.cmdarg = SECURE_ERASE; -	cmd.resp_type = MMC_RSP_R1b; - -	err = mmc_send_cmd(mmc, &cmd, NULL); -	if (err) -		goto err_out; - -	return 0; - -err_out: -	puts("mmc erase failed\n"); -	return err; -} - -static unsigned long -mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt) -{ -	int err = 0; -	struct mmc *mmc = find_mmc_device(dev_num); -	lbaint_t blk = 0, blk_r = 0; -	int timeout = 1000; - -	if (!mmc) -		return -1; - -	if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size)) -		printf("\n\nCaution! Your devices Erase group is 0x%x\n" -		       "The erase range would be change to " -		       "0x" LBAF "~0x" LBAF "\n\n", -		       mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1), -		       ((start + blkcnt + mmc->erase_grp_size) -		       & ~(mmc->erase_grp_size - 1)) - 1); - -	while (blk < blkcnt) { -		blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? -			mmc->erase_grp_size : (blkcnt - blk); -		err = mmc_erase_t(mmc, start + blk, blk_r); -		if (err) -			break; - -		blk += blk_r; - -		/* Waiting for the ready status */ -		if (mmc_send_status(mmc, timeout)) -			return 0; -	} - -	return blk; -} - -static ulong -mmc_write_blocks(struct mmc *mmc, lbaint_t start, lbaint_t blkcnt, const void*src) -{ -	struct mmc_cmd cmd; -	struct mmc_data data; -	int timeout = 1000; - -	if ((start + blkcnt) > mmc->block_dev.lba) { -		printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", -			start + blkcnt, mmc->block_dev.lba); -		return 0; -	} - -	if (blkcnt == 0) -		return 0; -	else if (blkcnt == 1) -		cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; -	else -		cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; - -	if (mmc->high_capacity) -		cmd.cmdarg = start; -	else -		cmd.cmdarg = start * mmc->write_bl_len; - -	cmd.resp_type = MMC_RSP_R1; - -	data.src = src; -	data.blocks = blkcnt; -	data.blocksize = mmc->write_bl_len; -	data.flags = MMC_DATA_WRITE; - -	if (mmc_send_cmd(mmc, &cmd, &data)) { -		printf("mmc write failed\n"); -		return 0; -	} - -	/* SPI multiblock writes terminate using a special -	 * token, not a STOP_TRANSMISSION request. -	 */ -	if (!mmc_host_is_spi(mmc) && blkcnt > 1) { -		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; -		cmd.cmdarg = 0; -		cmd.resp_type = MMC_RSP_R1b; -		if (mmc_send_cmd(mmc, &cmd, NULL)) { -			printf("mmc fail to send stop cmd\n"); -			return 0; -		} -	} - -	/* Waiting for the ready status */ -	if (mmc_send_status(mmc, timeout)) -		return 0; - -	return blkcnt; -} - -static ulong -mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, const void*src) -{ -	lbaint_t cur, blocks_todo = blkcnt; - -	struct mmc *mmc = find_mmc_device(dev_num); -	if (!mmc) -		return 0; - -	if (mmc_set_blocklen(mmc, mmc->write_bl_len)) -		return 0; - -	do { -		cur = (blocks_todo > mmc->b_max) ?  mmc->b_max : blocks_todo; -		if(mmc_write_blocks(mmc, start, cur, src) != cur) -			return 0; -		blocks_todo -= cur; -		start += cur; -		src += cur * mmc->write_bl_len; -	} while (blocks_todo > 0); - -	return blkcnt; -} -  static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,  			   lbaint_t blkcnt)  { @@ -385,7 +220,9 @@ static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,  		cmd.cmdarg = 0;  		cmd.resp_type = MMC_RSP_R1b;  		if (mmc_send_cmd(mmc, &cmd, NULL)) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)  			printf("mmc fail to send stop cmd\n"); +#endif  			return 0;  		}  	} @@ -405,8 +242,10 @@ static ulong mmc_bread(int dev_num, lbaint_t start, lbaint_t blkcnt, void *dst)  		return 0;  	if ((start + blkcnt) > mmc->block_dev.lba) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)  		printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",  			start + blkcnt, mmc->block_dev.lba); +#endif  		return 0;  	} @@ -1268,6 +1107,7 @@ static int mmc_startup(struct mmc *mmc)  	mmc->block_dev.blksz = mmc->read_bl_len;  	mmc->block_dev.log2blksz = LOG2(mmc->block_dev.blksz);  	mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len); +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)  	sprintf(mmc->block_dev.vendor, "Man %06x Snr %04x%04x",  		mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff),  		(mmc->cid[3] >> 16) & 0xffff); @@ -1277,6 +1117,11 @@ static int mmc_startup(struct mmc *mmc)  		(mmc->cid[2] >> 24) & 0xff);  	sprintf(mmc->block_dev.revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf,  		(mmc->cid[2] >> 16) & 0xf); +#else +	mmc->block_dev.vendor[0] = 0; +	mmc->block_dev.product[0] = 0; +	mmc->block_dev.revision[0] = 0; +#endif  #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)  	init_part(&mmc->block_dev);  #endif @@ -1343,7 +1188,9 @@ int mmc_start_init(struct mmc *mmc)  	if (mmc_getcd(mmc) == 0) {  		mmc->has_init = 0; +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)  		printf("MMC: no card present\n"); +#endif  		return NO_CARD_ERR;  	} @@ -1378,7 +1225,9 @@ int mmc_start_init(struct mmc *mmc)  		err = mmc_send_op_cond(mmc);  		if (err && err != IN_PROGRESS) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)  			printf("Card did not respond to voltage select!\n"); +#endif  			return UNUSABLE_ERR;  		}  	} @@ -1434,6 +1283,8 @@ static int __def_mmc_init(bd_t *bis)  int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));  int board_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init"))); +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) +  void print_mmc_devices(char separator)  {  	struct mmc *m; @@ -1451,6 +1302,10 @@ void print_mmc_devices(char separator)  	printf("\n");  } +#else +void print_mmc_devices(char separator) { } +#endif +  int get_mmc_num(void)  {  	return cur_dev_num; diff --git a/drivers/mmc/mmc_private.h b/drivers/mmc/mmc_private.h new file mode 100644 index 000000000..16dcf9ff6 --- /dev/null +++ b/drivers/mmc/mmc_private.h @@ -0,0 +1,45 @@ +/* + * Copyright 2008,2010 Freescale Semiconductor, Inc + * Andy Fleming + * + * Based (loosely) on the Linux code + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#ifndef _MMC_PRIVATE_H_ +#define _MMC_PRIVATE_H_ + +#include <mmc.h> + +extern int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, +			struct mmc_data *data); +extern int mmc_send_status(struct mmc *mmc, int timeout); +extern int mmc_set_blocklen(struct mmc *mmc, int len); + +#ifndef CONFIG_SPL_BUILD + +extern unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt); + +extern ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, +		const void *src); + +#else /* CONFIG_SPL_BUILD */ + +/* SPL will never write or erase, declare dummies to reduce code size. */ + +static inline unsigned long mmc_berase(int dev_num, lbaint_t start, +		lbaint_t blkcnt) +{ +	return 0; +} + +static inline ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, +		const void *src) +{ +	return 0; +} + +#endif /* CONFIG_SPL_BUILD */ + +#endif /* _MMC_PRIVATE_H_ */ diff --git a/drivers/mmc/mmc_write.c b/drivers/mmc/mmc_write.c new file mode 100644 index 000000000..aa2fdefa7 --- /dev/null +++ b/drivers/mmc/mmc_write.c @@ -0,0 +1,179 @@ +/* + * Copyright 2008, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the Linux code + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <config.h> +#include <common.h> +#include <part.h> +#include "mmc_private.h" + +static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt) +{ +	struct mmc_cmd cmd; +	ulong end; +	int err, start_cmd, end_cmd; + +	if (mmc->high_capacity) { +		end = start + blkcnt - 1; +	} else { +		end = (start + blkcnt - 1) * mmc->write_bl_len; +		start *= mmc->write_bl_len; +	} + +	if (IS_SD(mmc)) { +		start_cmd = SD_CMD_ERASE_WR_BLK_START; +		end_cmd = SD_CMD_ERASE_WR_BLK_END; +	} else { +		start_cmd = MMC_CMD_ERASE_GROUP_START; +		end_cmd = MMC_CMD_ERASE_GROUP_END; +	} + +	cmd.cmdidx = start_cmd; +	cmd.cmdarg = start; +	cmd.resp_type = MMC_RSP_R1; + +	err = mmc_send_cmd(mmc, &cmd, NULL); +	if (err) +		goto err_out; + +	cmd.cmdidx = end_cmd; +	cmd.cmdarg = end; + +	err = mmc_send_cmd(mmc, &cmd, NULL); +	if (err) +		goto err_out; + +	cmd.cmdidx = MMC_CMD_ERASE; +	cmd.cmdarg = SECURE_ERASE; +	cmd.resp_type = MMC_RSP_R1b; + +	err = mmc_send_cmd(mmc, &cmd, NULL); +	if (err) +		goto err_out; + +	return 0; + +err_out: +	puts("mmc erase failed\n"); +	return err; +} + +unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt) +{ +	int err = 0; +	struct mmc *mmc = find_mmc_device(dev_num); +	lbaint_t blk = 0, blk_r = 0; +	int timeout = 1000; + +	if (!mmc) +		return -1; + +	if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size)) +		printf("\n\nCaution! Your devices Erase group is 0x%x\n" +		       "The erase range would be change to " +		       "0x" LBAF "~0x" LBAF "\n\n", +		       mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1), +		       ((start + blkcnt + mmc->erase_grp_size) +		       & ~(mmc->erase_grp_size - 1)) - 1); + +	while (blk < blkcnt) { +		blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? +			mmc->erase_grp_size : (blkcnt - blk); +		err = mmc_erase_t(mmc, start + blk, blk_r); +		if (err) +			break; + +		blk += blk_r; + +		/* Waiting for the ready status */ +		if (mmc_send_status(mmc, timeout)) +			return 0; +	} + +	return blk; +} + +static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start, +		lbaint_t blkcnt, const void *src) +{ +	struct mmc_cmd cmd; +	struct mmc_data data; +	int timeout = 1000; + +	if ((start + blkcnt) > mmc->block_dev.lba) { +		printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", +		       start + blkcnt, mmc->block_dev.lba); +		return 0; +	} + +	if (blkcnt == 0) +		return 0; +	else if (blkcnt == 1) +		cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; +	else +		cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; + +	if (mmc->high_capacity) +		cmd.cmdarg = start; +	else +		cmd.cmdarg = start * mmc->write_bl_len; + +	cmd.resp_type = MMC_RSP_R1; + +	data.src = src; +	data.blocks = blkcnt; +	data.blocksize = mmc->write_bl_len; +	data.flags = MMC_DATA_WRITE; + +	if (mmc_send_cmd(mmc, &cmd, &data)) { +		printf("mmc write failed\n"); +		return 0; +	} + +	/* SPI multiblock writes terminate using a special +	 * token, not a STOP_TRANSMISSION request. +	 */ +	if (!mmc_host_is_spi(mmc) && blkcnt > 1) { +		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; +		cmd.cmdarg = 0; +		cmd.resp_type = MMC_RSP_R1b; +		if (mmc_send_cmd(mmc, &cmd, NULL)) { +			printf("mmc fail to send stop cmd\n"); +			return 0; +		} +	} + +	/* Waiting for the ready status */ +	if (mmc_send_status(mmc, timeout)) +		return 0; + +	return blkcnt; +} + +ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, const void *src) +{ +	lbaint_t cur, blocks_todo = blkcnt; + +	struct mmc *mmc = find_mmc_device(dev_num); +	if (!mmc) +		return 0; + +	if (mmc_set_blocklen(mmc, mmc->write_bl_len)) +		return 0; + +	do { +		cur = (blocks_todo > mmc->b_max) ?  mmc->b_max : blocks_todo; +		if (mmc_write_blocks(mmc, start, cur, src) != cur) +			return 0; +		blocks_todo -= cur; +		start += cur; +		src += cur * mmc->write_bl_len; +	} while (blocks_todo > 0); + +	return blkcnt; +} diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 975b2c5ba..d3a8b5303 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -288,6 +288,30 @@ static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit)  	mmc_reg_out(&mmc_base->sysctl, bit, bit); +	/* +	 * CMD(DAT) lines reset procedures are slightly different +	 * for OMAP3 and OMAP4(AM335x,OMAP5,DRA7xx). +	 * According to OMAP3 TRM: +	 * Set SRC(SRD) bit in MMCHS_SYSCTL register to 0x1 and wait until it +	 * returns to 0x0. +	 * According to OMAP4(AM335x,OMAP5,DRA7xx) TRMs, CMD(DATA) lines reset +	 * procedure steps must be as follows: +	 * 1. Initiate CMD(DAT) line reset by writing 0x1 to SRC(SRD) bit in +	 *    MMCHS_SYSCTL register (SD_SYSCTL for AM335x). +	 * 2. Poll the SRC(SRD) bit until it is set to 0x1. +	 * 3. Wait until the SRC (SRD) bit returns to 0x0 +	 *    (reset procedure is completed). +	 */ +#if defined(CONFIG_OMAP44XX) || defined(CONFIG_OMAP54XX) || \ +	defined(CONFIG_AM33XX) +	if (!(readl(&mmc_base->sysctl) & bit)) { +		start = get_timer(0); +		while (!(readl(&mmc_base->sysctl) & bit)) { +			if (get_timer(0) - start > MAX_RETRY_MS) +				return; +		} +	} +#endif  	start = get_timer(0);  	while ((readl(&mmc_base->sysctl) & bit) != 0) {  		if (get_timer(0) - start > MAX_RETRY_MS) { @@ -376,6 +400,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,  	}  	writel(cmd->cmdarg, &mmc_base->arg); +	udelay(20);		/* To fix "No status update" error on eMMC */  	writel((cmd->cmdidx << 24) | flags, &mmc_base->cmd);  	start = get_timer(0); @@ -480,7 +505,7 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,  	unsigned int count;  	/* -	 * Start Polled Read +	 * Start Polled Write  	 */  	count = (size > MMCSD_SECTOR_SIZE) ? MMCSD_SECTOR_SIZE : size;  	count /= 4; @@ -586,6 +611,8 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio,  {  	struct mmc *mmc = &hsmmc_dev[dev_index];  	struct omap_hsmmc_data *priv_data = &hsmmc_dev_data[dev_index]; +	uint host_caps_val = MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS | +			     MMC_MODE_HC;  	sprintf(mmc->name, "OMAP SD/MMC");  	mmc->send_cmd = mmc_send_cmd; @@ -600,11 +627,20 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio,  #ifdef OMAP_HSMMC2_BASE  	case 1:  		priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC2_BASE; +#if (defined(CONFIG_OMAP44XX) || defined(CONFIG_OMAP54XX) || \ +     defined(CONFIG_DRA7XX)) && defined(CONFIG_HSMMC2_8BIT) +		/* Enable 8-bit interface for eMMC on OMAP4/5 or DRA7XX */ +		host_caps_val |= MMC_MODE_8BIT; +#endif  		break;  #endif  #ifdef OMAP_HSMMC3_BASE  	case 2:  		priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC3_BASE; +#if defined(CONFIG_DRA7XX) && defined(CONFIG_HSMMC3_8BIT) +		/* Enable 8-bit interface for eMMC on DRA7XX */ +		host_caps_val |= MMC_MODE_8BIT; +#endif  		break;  #endif  	default: @@ -620,8 +656,7 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio,  		mmc->getwp = omap_mmc_getwp;  	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; -	mmc->host_caps = (MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS | -				MMC_MODE_HC) & ~host_caps_mask; +	mmc->host_caps = host_caps_val & ~host_caps_mask;  	mmc->f_min = 400000; diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c index 7f89403b4..40ff8739b 100644 --- a/drivers/mmc/s5p_sdhci.c +++ b/drivers/mmc/s5p_sdhci.c @@ -72,7 +72,7 @@ int s5p_sdhci_init(u32 regbase, int index, int bus_width)  	host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE |  		SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR | -		SDHCI_QUIRK_WAIT_SEND_CMD; +		SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_USE_WIDE8;  	host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;  	host->version = sdhci_readw(host, SDHCI_HOST_VERSION); @@ -81,6 +81,8 @@ int s5p_sdhci_init(u32 regbase, int index, int bus_width)  	host->index = index;  	host->host_caps = MMC_MODE_HC; +	if (bus_width == 8) +		host->host_caps |= MMC_MODE_8BIT;  	return add_sdhci(host, 52000000, 400000);  } diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 42619916e..dfb2eeeb4 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -68,10 +68,9 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data,  	unsigned int stat, rdy, mask, timeout, block = 0;  #ifdef CONFIG_MMC_SDMA  	unsigned char ctrl; -	ctrl = sdhci_readl(host, SDHCI_HOST_CONTROL); +	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);  	ctrl &= ~SDHCI_CTRL_DMA_MASK; -	ctrl |= SDHCI_CTRL_SDMA; -	sdhci_writel(host, ctrl, SDHCI_HOST_CONTROL); +	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);  #endif  	timeout = 1000000; @@ -254,7 +253,7 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)  	if (clock == 0)  		return 0; -	if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) { +	if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) {  		/* Version 3.00 divisors must be a multiple of 2. */  		if (mmc->f_max <= clock)  			div = 1; @@ -347,10 +346,11 @@ void sdhci_set_ios(struct mmc *mmc)  	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);  	if (mmc->bus_width == 8) {  		ctrl &= ~SDHCI_CTRL_4BITBUS; -		if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) +		if ((SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) || +				(host->quirks & SDHCI_QUIRK_USE_WIDE8))  			ctrl |= SDHCI_CTRL_8BITBUS;  	} else { -		if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) +		if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300)  			ctrl &= ~SDHCI_CTRL_8BITBUS;  		if (mmc->bus_width == 4)  			ctrl |= SDHCI_CTRL_4BITBUS; @@ -437,7 +437,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)  	if (max_clk)  		mmc->f_max = max_clk;  	else { -		if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) +		if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300)  			mmc->f_max = (caps & SDHCI_CLOCK_V3_BASE_MASK)  				>> SDHCI_CLOCK_BASE_SHIFT;  		else @@ -452,7 +452,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)  	if (min_clk)  		mmc->f_min = min_clk;  	else { -		if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) +		if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300)  			mmc->f_min = mmc->f_max / SDHCI_MAX_DIV_SPEC_300;  		else  			mmc->f_min = mmc->f_max / SDHCI_MAX_DIV_SPEC_200; @@ -470,7 +470,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)  		mmc->voltages |= host->voltages;  	mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT; -	if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) { +	if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) {  		if (caps & SDHCI_CAN_DO_8BIT)  			mmc->host_caps |= MMC_MODE_8BIT;  	} diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 49c08145a..deda5f244 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -217,11 +217,23 @@ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)  int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,  	     u_char *buf)  { +	int ret_code;  	if (from < 0 || from > mtd->size || len > mtd->size - from)  		return -EINVAL;  	if (!len)  		return 0; -	return mtd->_read(mtd, from, len, retlen, buf); + +	/* +	 * In the absence of an error, drivers return a non-negative integer +	 * representing the maximum number of bitflips that were corrected on +	 * any one ecc region (if applicable; zero otherwise). +	 */ +	ret_code = mtd->_read(mtd, from, len, retlen, buf); +	if (unlikely(ret_code < 0)) +		return ret_code; +	if (mtd->ecc_strength == 0) +		return 0;	/* device lacks ecc */ +	return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0;  }  int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 9dfe7bbc9..146ce11eb 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -53,12 +53,12 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,  	stats = part->master->ecc_stats;  	res = mtd_read(part->master, from + part->offset, len, retlen, buf); -	if (unlikely(res)) { -		if (mtd_is_bitflip(res)) -			mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected; -		if (mtd_is_eccerr(res)) -			mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed; -	} +	if (unlikely(mtd_is_eccerr(res))) +		mtd->ecc_stats.failed += +			part->master->ecc_stats.failed - stats.failed; +	else +		mtd->ecc_stats.corrected += +			part->master->ecc_stats.corrected - stats.corrected;  	return res;  } diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 9e05cef41..d4d586c94 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1238,6 +1238,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,  		mtd->oobavail : mtd->oobsize;  	uint8_t *bufpoi, *oob, *buf; +	unsigned int max_bitflips = 0;  	stats = mtd->ecc_stats; @@ -1265,7 +1266,10 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,  			chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); -			/* Now read the page into the buffer */ +			/* +			 * Now read the page into the buffer.  Absent an error, +			 * the read methods return max bitflips per ecc step. +			 */  			if (unlikely(ops->mode == MTD_OPS_RAW))  				ret = chip->ecc.read_page_raw(mtd, chip, bufpoi,  							      oob_required, @@ -1284,15 +1288,19 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,  				break;  			} +			max_bitflips = max_t(unsigned int, max_bitflips, ret); +  			/* Transfer not aligned data */  			if (!aligned) {  				if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&  				    !(mtd->ecc_stats.failed - stats.failed) && -				    (ops->mode != MTD_OPS_RAW)) +				    (ops->mode != MTD_OPS_RAW)) {  					chip->pagebuf = realpage; -				else +					chip->pagebuf_bitflips = ret; +				} else {  					/* Invalidate page cache */  					chip->pagebuf = -1; +				}  				memcpy(buf, chip->buffers->databuf + col, bytes);  			} @@ -1310,6 +1318,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,  		} else {  			memcpy(buf, chip->buffers->databuf + col, bytes);  			buf += bytes; +			max_bitflips = max_t(unsigned int, max_bitflips, +					     chip->pagebuf_bitflips);  		}  		readlen -= bytes; @@ -1341,7 +1351,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,  	if (mtd->ecc_stats.failed - stats.failed)  		return -EBADMSG; -	return  mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; +	return max_bitflips;  }  /** diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index ddfe7e7c7..067f8ef18 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -969,7 +969,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,  	if (mtd->ecc_stats.failed - stats.failed)  		return -EBADMSG; -	return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; +	/* return max bitflips per ecc step; ONENANDs correct 1 bit only */ +	return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0;  }  /** diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 191138ad1..86ffc59d0 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -14,16 +14,11 @@ COBJS-$(CONFIG_SPL_SPI_LOAD)	+= spi_spl_load.o  COBJS-$(CONFIG_SPL_SPI_BOOT)	+= fsl_espi_spl.o  endif -COBJS-$(CONFIG_SPI_FLASH)	+= spi_flash.o -COBJS-$(CONFIG_SPI_FLASH_ATMEL)	+= atmel.o -COBJS-$(CONFIG_SPI_FLASH_EON)	+= eon.o -COBJS-$(CONFIG_SPI_FLASH_GIGADEVICE)	+= gigadevice.o -COBJS-$(CONFIG_SPI_FLASH_MACRONIX)	+= macronix.o -COBJS-$(CONFIG_SPI_FLASH_SPANSION)	+= spansion.o -COBJS-$(CONFIG_SPI_FLASH_SST)	+= sst.o -COBJS-$(CONFIG_SPI_FLASH_STMICRO)	+= stmicro.o -COBJS-$(CONFIG_SPI_FLASH_WINBOND)	+= winbond.o -COBJS-$(CONFIG_SPI_FRAM_RAMTRON)	+= ramtron.o +ifdef CONFIG_CMD_SF +COBJS-y        += sf.o +endif +COBJS-$(CONFIG_SPI_FLASH) += sf_probe.o sf_ops.o +COBJS-$(CONFIG_SPI_FRAM_RAMTRON) += ramtron.o  COBJS-$(CONFIG_SPI_M95XXX) += eeprom_m95xxx.o  COBJS	:= $(COBJS-y) diff --git a/drivers/mtd/spi/atmel.c b/drivers/mtd/spi/atmel.c deleted file mode 100644 index f34df43f5..000000000 --- a/drivers/mtd/spi/atmel.c +++ /dev/null @@ -1,544 +0,0 @@ -/* - * Atmel SPI DataFlash support - * - * Copyright (C) 2008 Atmel Corporation - * Licensed under the GPL-2 or later. - */ - -#include <common.h> -#include <malloc.h> -#include <spi_flash.h> - -#include "spi_flash_internal.h" - -/* AT45-specific commands */ -#define CMD_AT45_READ_STATUS		0xd7 -#define CMD_AT45_ERASE_PAGE		0x81 -#define CMD_AT45_LOAD_PROG_BUF1		0x82 -#define CMD_AT45_LOAD_BUF1		0x84 -#define CMD_AT45_LOAD_PROG_BUF2		0x85 -#define CMD_AT45_LOAD_BUF2		0x87 -#define CMD_AT45_PROG_BUF1		0x88 -#define CMD_AT45_PROG_BUF2		0x89 - -/* AT45 status register bits */ -#define AT45_STATUS_P2_PAGE_SIZE	(1 << 0) -#define AT45_STATUS_READY		(1 << 7) - -/* DataFlash family IDs, as obtained from the second idcode byte */ -#define DF_FAMILY_AT26F			0 -#define DF_FAMILY_AT45			1 -#define DF_FAMILY_AT26DF		2	/* AT25DF and AT26DF */ - -struct atmel_spi_flash_params { -	u8		idcode1; -	/* Log2 of page size in power-of-two mode */ -	u8		l2_page_size; -	u8		pages_per_block; -	u8		blocks_per_sector; -	u8		nr_sectors; -	const char	*name; -}; - -/* spi_flash needs to be first so upper layers can free() it */ -struct atmel_spi_flash { -	struct spi_flash flash; -	const struct atmel_spi_flash_params *params; -}; - -static inline struct atmel_spi_flash * -to_atmel_spi_flash(struct spi_flash *flash) -{ -	return container_of(flash, struct atmel_spi_flash, flash); -} - -static const struct atmel_spi_flash_params atmel_spi_flash_table[] = { -	{ -		.idcode1		= 0x22, -		.l2_page_size		= 8, -		.pages_per_block	= 8, -		.blocks_per_sector	= 16, -		.nr_sectors		= 4, -		.name			= "AT45DB011D", -	}, -	{ -		.idcode1		= 0x23, -		.l2_page_size		= 8, -		.pages_per_block	= 8, -		.blocks_per_sector	= 16, -		.nr_sectors		= 8, -		.name			= "AT45DB021D", -	}, -	{ -		.idcode1		= 0x24, -		.l2_page_size		= 8, -		.pages_per_block	= 8, -		.blocks_per_sector	= 32, -		.nr_sectors		= 8, -		.name			= "AT45DB041D", -	}, -	{ -		.idcode1		= 0x25, -		.l2_page_size		= 8, -		.pages_per_block	= 8, -		.blocks_per_sector	= 32, -		.nr_sectors		= 16, -		.name			= "AT45DB081D", -	}, -	{ -		.idcode1		= 0x26, -		.l2_page_size		= 9, -		.pages_per_block	= 8, -		.blocks_per_sector	= 32, -		.nr_sectors		= 16, -		.name			= "AT45DB161D", -	}, -	{ -		.idcode1		= 0x27, -		.l2_page_size		= 9, -		.pages_per_block	= 8, -		.blocks_per_sector	= 64, -		.nr_sectors		= 64, -		.name			= "AT45DB321D", -	}, -	{ -		.idcode1		= 0x28, -		.l2_page_size		= 10, -		.pages_per_block	= 8, -		.blocks_per_sector	= 32, -		.nr_sectors		= 32, -		.name			= "AT45DB642D", -	}, -	{ -		.idcode1		= 0x47, -		.l2_page_size		= 8, -		.pages_per_block	= 16, -		.blocks_per_sector	= 16, -		.nr_sectors		= 64, -		.name			= "AT25DF321", -	}, -}; - -static int at45_wait_ready(struct spi_flash *flash, unsigned long timeout) -{ -	struct spi_slave *spi = flash->spi; -	unsigned long timebase; -	int ret; -	u8 cmd = CMD_AT45_READ_STATUS; -	u8 status; - -	timebase = get_timer(0); - -	ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); -	if (ret) -		return -1; - -	do { -		ret = spi_xfer(spi, 8, NULL, &status, 0); -		if (ret) -			return -1; - -		if (status & AT45_STATUS_READY) -			break; -	} while (get_timer(timebase) < timeout); - -	/* Deactivate CS */ -	spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); - -	if (status & AT45_STATUS_READY) -		return 0; - -	/* Timed out */ -	return -1; -} - -/* - * Assemble the address part of a command for AT45 devices in - * non-power-of-two page size mode. - */ -static void at45_build_address(struct atmel_spi_flash *asf, u8 *cmd, u32 offset) -{ -	unsigned long page_addr; -	unsigned long byte_addr; -	unsigned long page_size; -	unsigned int page_shift; - -	/* -	 * The "extra" space per page is the power-of-two page size -	 * divided by 32. -	 */ -	page_shift = asf->params->l2_page_size; -	page_size = (1 << page_shift) + (1 << (page_shift - 5)); -	page_shift++; -	page_addr = offset / page_size; -	byte_addr = offset % page_size; - -	cmd[0] = page_addr >> (16 - page_shift); -	cmd[1] = page_addr << (page_shift - 8) | (byte_addr >> 8); -	cmd[2] = byte_addr; -} - -static int dataflash_read_fast_at45(struct spi_flash *flash, -		u32 offset, size_t len, void *buf) -{ -	struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); -	u8 cmd[5]; - -	cmd[0] = CMD_READ_ARRAY_FAST; -	at45_build_address(asf, cmd + 1, offset); -	cmd[4] = 0x00; - -	return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); -} - -/* - * TODO: the two write funcs (_p2/_at45) should get unified ... - */ -static int dataflash_write_p2(struct spi_flash *flash, -		u32 offset, size_t len, const void *buf) -{ -	struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); -	unsigned long page_size; -	u32 addr = offset; -	size_t chunk_len; -	size_t actual; -	int ret; -	u8 cmd[4]; - -	/* -	 * TODO: This function currently uses only page buffer #1.  We can -	 * speed this up by using both buffers and loading one buffer while -	 * the other is being programmed into main memory. -	 */ - -	page_size = (1 << asf->params->l2_page_size); - -	ret = spi_claim_bus(flash->spi); -	if (ret) { -		debug("SF: Unable to claim SPI bus\n"); -		return ret; -	} - -	for (actual = 0; actual < len; actual += chunk_len) { -		chunk_len = min(len - actual, page_size - (addr % page_size)); - -		/* Use the same address bits for both commands */ -		cmd[0] = CMD_AT45_LOAD_BUF1; -		cmd[1] = addr >> 16; -		cmd[2] = addr >> 8; -		cmd[3] = addr; - -		ret = spi_flash_cmd_write(flash->spi, cmd, 4, -				buf + actual, chunk_len); -		if (ret < 0) { -			debug("SF: Loading AT45 buffer failed\n"); -			goto out; -		} - -		cmd[0] = CMD_AT45_PROG_BUF1; -		ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); -		if (ret < 0) { -			debug("SF: AT45 page programming failed\n"); -			goto out; -		} - -		ret = at45_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); -		if (ret < 0) { -			debug("SF: AT45 page programming timed out\n"); -			goto out; -		} - -		addr += chunk_len; -	} - -	debug("SF: AT45: Successfully programmed %zu bytes @ 0x%x\n", -	      len, offset); -	ret = 0; - -out: -	spi_release_bus(flash->spi); -	return ret; -} - -static int dataflash_write_at45(struct spi_flash *flash, -		u32 offset, size_t len, const void *buf) -{ -	struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); -	unsigned long page_addr; -	unsigned long byte_addr; -	unsigned long page_size; -	unsigned int page_shift; -	size_t chunk_len; -	size_t actual; -	int ret; -	u8 cmd[4]; - -	/* -	 * TODO: This function currently uses only page buffer #1.  We can -	 * speed this up by using both buffers and loading one buffer while -	 * the other is being programmed into main memory. -	 */ - -	page_shift = asf->params->l2_page_size; -	page_size = (1 << page_shift) + (1 << (page_shift - 5)); -	page_shift++; -	page_addr = offset / page_size; -	byte_addr = offset % page_size; - -	ret = spi_claim_bus(flash->spi); -	if (ret) { -		debug("SF: Unable to claim SPI bus\n"); -		return ret; -	} - -	for (actual = 0; actual < len; actual += chunk_len) { -		chunk_len = min(len - actual, page_size - byte_addr); - -		/* Use the same address bits for both commands */ -		cmd[0] = CMD_AT45_LOAD_BUF1; -		cmd[1] = page_addr >> (16 - page_shift); -		cmd[2] = page_addr << (page_shift - 8) | (byte_addr >> 8); -		cmd[3] = byte_addr; - -		ret = spi_flash_cmd_write(flash->spi, cmd, 4, -				buf + actual, chunk_len); -		if (ret < 0) { -			debug("SF: Loading AT45 buffer failed\n"); -			goto out; -		} - -		cmd[0] = CMD_AT45_PROG_BUF1; -		ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); -		if (ret < 0) { -			debug("SF: AT45 page programming failed\n"); -			goto out; -		} - -		ret = at45_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); -		if (ret < 0) { -			debug("SF: AT45 page programming timed out\n"); -			goto out; -		} - -		page_addr++; -		byte_addr = 0; -	} - -	debug("SF: AT45: Successfully programmed %zu bytes @ 0x%x\n", -	      len, offset); -	ret = 0; - -out: -	spi_release_bus(flash->spi); -	return ret; -} - -/* - * TODO: the two erase funcs (_p2/_at45) should get unified ... - */ -static int dataflash_erase_p2(struct spi_flash *flash, u32 offset, size_t len) -{ -	struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); -	unsigned long page_size; - -	size_t actual; -	int ret; -	u8 cmd[4]; - -	/* -	 * TODO: This function currently uses page erase only. We can -	 * probably speed things up by using block and/or sector erase -	 * when possible. -	 */ - -	page_size = (1 << asf->params->l2_page_size); - -	if (offset % page_size || len % page_size) { -		debug("SF: Erase offset/length not multiple of page size\n"); -		return -1; -	} - -	cmd[0] = CMD_AT45_ERASE_PAGE; -	cmd[3] = 0x00; - -	ret = spi_claim_bus(flash->spi); -	if (ret) { -		debug("SF: Unable to claim SPI bus\n"); -		return ret; -	} - -	for (actual = 0; actual < len; actual += page_size) { -		cmd[1] = offset >> 16; -		cmd[2] = offset >> 8; - -		ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); -		if (ret < 0) { -			debug("SF: AT45 page erase failed\n"); -			goto out; -		} - -		ret = at45_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); -		if (ret < 0) { -			debug("SF: AT45 page erase timed out\n"); -			goto out; -		} - -		offset += page_size; -	} - -	debug("SF: AT45: Successfully erased %zu bytes @ 0x%x\n", -	      len, offset); -	ret = 0; - -out: -	spi_release_bus(flash->spi); -	return ret; -} - -static int dataflash_erase_at45(struct spi_flash *flash, u32 offset, size_t len) -{ -	struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); -	unsigned long page_addr; -	unsigned long page_size; -	unsigned int page_shift; -	size_t actual; -	int ret; -	u8 cmd[4]; - -	/* -	 * TODO: This function currently uses page erase only. We can -	 * probably speed things up by using block and/or sector erase -	 * when possible. -	 */ - -	page_shift = asf->params->l2_page_size; -	page_size = (1 << page_shift) + (1 << (page_shift - 5)); -	page_shift++; -	page_addr = offset / page_size; - -	if (offset % page_size || len % page_size) { -		debug("SF: Erase offset/length not multiple of page size\n"); -		return -1; -	} - -	cmd[0] = CMD_AT45_ERASE_PAGE; -	cmd[3] = 0x00; - -	ret = spi_claim_bus(flash->spi); -	if (ret) { -		debug("SF: Unable to claim SPI bus\n"); -		return ret; -	} - -	for (actual = 0; actual < len; actual += page_size) { -		cmd[1] = page_addr >> (16 - page_shift); -		cmd[2] = page_addr << (page_shift - 8); - -		ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); -		if (ret < 0) { -			debug("SF: AT45 page erase failed\n"); -			goto out; -		} - -		ret = at45_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); -		if (ret < 0) { -			debug("SF: AT45 page erase timed out\n"); -			goto out; -		} - -		page_addr++; -	} - -	debug("SF: AT45: Successfully erased %zu bytes @ 0x%x\n", -	      len, offset); -	ret = 0; - -out: -	spi_release_bus(flash->spi); -	return ret; -} - -struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode) -{ -	const struct atmel_spi_flash_params *params; -	unsigned page_size; -	unsigned int family; -	struct atmel_spi_flash *asf; -	unsigned int i; -	int ret; -	u8 status; - -	for (i = 0; i < ARRAY_SIZE(atmel_spi_flash_table); i++) { -		params = &atmel_spi_flash_table[i]; -		if (params->idcode1 == idcode[1]) -			break; -	} - -	if (i == ARRAY_SIZE(atmel_spi_flash_table)) { -		debug("SF: Unsupported DataFlash ID %02x\n", -		      idcode[1]); -		return NULL; -	} - -	asf = spi_flash_alloc(struct atmel_spi_flash, spi, params->name); -	if (!asf) { -		debug("SF: Failed to allocate memory\n"); -		return NULL; -	} - -	asf->params = params; - -	/* Assuming power-of-two page size initially. */ -	page_size = 1 << params->l2_page_size; - -	family = idcode[1] >> 5; - -	switch (family) { -	case DF_FAMILY_AT45: -		/* -		 * AT45 chips have configurable page size. The status -		 * register indicates which configuration is active. -		 */ -		ret = spi_flash_cmd(spi, CMD_AT45_READ_STATUS, &status, 1); -		if (ret) -			goto err; - -		debug("SF: AT45 status register: %02x\n", status); - -		if (!(status & AT45_STATUS_P2_PAGE_SIZE)) { -			asf->flash.read = dataflash_read_fast_at45; -			asf->flash.write = dataflash_write_at45; -			asf->flash.erase = dataflash_erase_at45; -			page_size += 1 << (params->l2_page_size - 5); -		} else { -			asf->flash.write = dataflash_write_p2; -			asf->flash.erase = dataflash_erase_p2; -		} - -		asf->flash.page_size = page_size; -		asf->flash.sector_size = page_size; -		break; - -	case DF_FAMILY_AT26F: -	case DF_FAMILY_AT26DF: -		asf->flash.page_size = page_size; -		asf->flash.sector_size = 4096; -		/* clear SPRL# bit for locked flash */ -		spi_flash_cmd_write_status(&asf->flash, 0); -		break; - -	default: -		debug("SF: Unsupported DataFlash family %u\n", family); -		goto err; -	} - -	asf->flash.size = page_size * params->pages_per_block -				* params->blocks_per_sector -				* params->nr_sectors; - -	return &asf->flash; - -err: -	free(asf); -	return NULL; -} diff --git a/drivers/mtd/spi/eon.c b/drivers/mtd/spi/eon.c deleted file mode 100644 index 25cfc1252..000000000 --- a/drivers/mtd/spi/eon.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * (C) Copyright 2010, ucRobotics Inc. - * Author: Chong Huang <chuang@ucrobotics.com> - * Licensed under the GPL-2 or later. - */ - -#include <common.h> -#include <malloc.h> -#include <spi_flash.h> - -#include "spi_flash_internal.h" - -struct eon_spi_flash_params { -	u8 idcode1; -	u16 nr_sectors; -	const char *name; -}; - -static const struct eon_spi_flash_params eon_spi_flash_table[] = { -	{ -		.idcode1 = 0x16, -		.nr_sectors = 1024, -		.name = "EN25Q32B", -	}, -	{ -		.idcode1 = 0x18, -		.nr_sectors = 4096, -		.name = "EN25Q128", -	}, -}; - -struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode) -{ -	const struct eon_spi_flash_params *params; -	struct spi_flash *flash; -	unsigned int i; - -	for (i = 0; i < ARRAY_SIZE(eon_spi_flash_table); ++i) { -		params = &eon_spi_flash_table[i]; -		if (params->idcode1 == idcode[2]) -			break; -	} - -	if (i == ARRAY_SIZE(eon_spi_flash_table)) { -		debug("SF: Unsupported EON ID %02x\n", idcode[1]); -		return NULL; -	} - -	flash = spi_flash_alloc_base(spi, params->name); -	if (!flash) { -		debug("SF: Failed to allocate memory\n"); -		return NULL; -	} - -	flash->page_size = 256; -	flash->sector_size = 256 * 16 * 16; -	flash->size = 256 * 16 * params->nr_sectors; - -	return flash; -} diff --git a/drivers/mtd/spi/gigadevice.c b/drivers/mtd/spi/gigadevice.c deleted file mode 100644 index b42581a70..000000000 --- a/drivers/mtd/spi/gigadevice.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Gigadevice SPI flash driver - * Copyright 2013, Samsung Electronics Co., Ltd. - * Author: Banajit Goswami <banajit.g@samsung.com> - * - * SPDX-License-Identifier:	GPL-2.0+ - */ - -#include <common.h> -#include <malloc.h> -#include <spi_flash.h> - -#include "spi_flash_internal.h" - -struct gigadevice_spi_flash_params { -	uint16_t	id; -	uint16_t	nr_blocks; -	const char	*name; -}; - -static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = { -	{ -		.id			= 0x6016, -		.nr_blocks		= 64, -		.name			= "GD25LQ", -	}, -	{ -		.id			= 0x4017, -		.nr_blocks		= 128, -		.name			= "GD25Q64B", -	}, -}; - -struct spi_flash *spi_flash_probe_gigadevice(struct spi_slave *spi, u8 *idcode) -{ -	const struct gigadevice_spi_flash_params *params; -	struct spi_flash *flash; -	unsigned int i; - -	for (i = 0; i < ARRAY_SIZE(gigadevice_spi_flash_table); i++) { -		params = &gigadevice_spi_flash_table[i]; -		if (params->id == ((idcode[1] << 8) | idcode[2])) -			break; -	} - -	if (i == ARRAY_SIZE(gigadevice_spi_flash_table)) { -		debug("SF: Unsupported Gigadevice ID %02x%02x\n", -		      idcode[1], idcode[2]); -		return NULL; -	} - -	flash = spi_flash_alloc_base(spi, params->name); -	if (!flash) { -		debug("SF: Failed to allocate memory\n"); -		return NULL; -	} -	/* page_size */ -	flash->page_size = 256; -	/* sector_size = page_size * pages_per_sector */ -	flash->sector_size = flash->page_size * 16; -	/* size = sector_size * sector_per_block * number of blocks */ -	flash->size = flash->sector_size * 16 * params->nr_blocks; - -	return flash; -} diff --git a/drivers/mtd/spi/macronix.c b/drivers/mtd/spi/macronix.c deleted file mode 100644 index 70435eb5a..000000000 --- a/drivers/mtd/spi/macronix.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2009(C) Marvell International Ltd. and its affiliates - * Prafulla Wadaskar <prafulla@marvell.com> - * - * Based on drivers/mtd/spi/stmicro.c - * - * Copyright 2008, Network Appliance Inc. - * Jason McMullan <mcmullan@netapp.com> - * - * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. - * TsiChung Liew (Tsi-Chung.Liew@freescale.com) - * - * SPDX-License-Identifier:	GPL-2.0+ - */ - -#include <common.h> -#include <malloc.h> -#include <spi_flash.h> - -#include "spi_flash_internal.h" - -struct macronix_spi_flash_params { -	u16 idcode; -	u16 nr_blocks; -	const char *name; -}; - -static const struct macronix_spi_flash_params macronix_spi_flash_table[] = { -	{ -		.idcode = 0x2013, -		.nr_blocks = 8, -		.name = "MX25L4005", -	}, -	{ -		.idcode = 0x2014, -		.nr_blocks = 16, -		.name = "MX25L8005", -	}, -	{ -		.idcode = 0x2015, -		.nr_blocks = 32, -		.name = "MX25L1605D", -	}, -	{ -		.idcode = 0x2016, -		.nr_blocks = 64, -		.name = "MX25L3205D", -	}, -	{ -		.idcode = 0x2017, -		.nr_blocks = 128, -		.name = "MX25L6405D", -	}, -	{ -		.idcode = 0x2018, -		.nr_blocks = 256, -		.name = "MX25L12805D", -	}, -	{ -		.idcode = 0x2618, -		.nr_blocks = 256, -		.name = "MX25L12855E", -	}, -}; - -struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode) -{ -	const struct macronix_spi_flash_params *params; -	struct spi_flash *flash; -	unsigned int i; -	u16 id = idcode[2] | idcode[1] << 8; - -	for (i = 0; i < ARRAY_SIZE(macronix_spi_flash_table); i++) { -		params = ¯onix_spi_flash_table[i]; -		if (params->idcode == id) -			break; -	} - -	if (i == ARRAY_SIZE(macronix_spi_flash_table)) { -		debug("SF: Unsupported Macronix ID %04x\n", id); -		return NULL; -	} - -	flash = spi_flash_alloc_base(spi, params->name); -	if (!flash) { -		debug("SF: Failed to allocate memory\n"); -		return NULL; -	} - -	flash->page_size = 256; -	flash->sector_size = 256 * 16 * 16; -	flash->size = flash->sector_size * params->nr_blocks; - -	/* Clear BP# bits for read-only flash */ -	spi_flash_cmd_write_status(flash, 0); - -	return flash; -} diff --git a/drivers/mtd/spi/ramtron.c b/drivers/mtd/spi/ramtron.c index 38f9d6916..d50da37c8 100644 --- a/drivers/mtd/spi/ramtron.c +++ b/drivers/mtd/spi/ramtron.c @@ -36,7 +36,7 @@  #include <common.h>  #include <malloc.h>  #include <spi_flash.h> -#include "spi_flash_internal.h" +#include "sf_internal.h"  /*   * Properties of supported FRAMs @@ -214,7 +214,8 @@ static int ramtron_erase(struct spi_flash *flash, u32 offset, size_t len)   * nore: we are called here with idcode pointing to the first non-0x7f byte   * already!   */ -struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, u8 *idcode) +static struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, +		u8 *idcode)  {  	const struct ramtron_spi_fram_params *params;  	struct ramtron_spi_fram *sn; @@ -270,7 +271,7 @@ struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, u8 *idcode)  	return NULL;  found: -	sn = spi_flash_alloc(struct ramtron_spi_fram, spi, params->name); +	sn = malloc(sizeof(*sn));  	if (!sn) {  		debug("SF: Failed to allocate memory\n");  		return NULL; @@ -285,3 +286,118 @@ found:  	return &sn->flash;  } + +/* + * The following table holds all device probe functions + * (All flashes are removed and implemented a common probe at + *  spi_flash_probe.c) + * + * shift:  number of continuation bytes before the ID + * idcode: the expected IDCODE or 0xff for non JEDEC devices + * probe:  the function to call + * + * Non JEDEC devices should be ordered in the table such that + * the probe functions with best detection algorithms come first. + * + * Several matching entries are permitted, they will be tried + * in sequence until a probe function returns non NULL. + * + * IDCODE_CONT_LEN may be redefined if a device needs to declare a + * larger "shift" value.  IDCODE_PART_LEN generally shouldn't be + * changed.  This is the max number of bytes probe functions may + * examine when looking up part-specific identification info. + * + * Probe functions will be given the idcode buffer starting at their + * manu id byte (the "idcode" in the table below).  In other words, + * all of the continuation bytes will be skipped (the "shift" below). + */ +#define IDCODE_CONT_LEN 0 +#define IDCODE_PART_LEN 5 +static const struct { +	const u8 shift; +	const u8 idcode; +	struct spi_flash *(*probe) (struct spi_slave *spi, u8 *idcode); +} flashes[] = { +	/* Keep it sorted by define name */ +#ifdef CONFIG_SPI_FRAM_RAMTRON +	{ 6, 0xc2, spi_fram_probe_ramtron, }, +# undef IDCODE_CONT_LEN +# define IDCODE_CONT_LEN 6 +#endif +#ifdef CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC +	{ 0, 0xff, spi_fram_probe_ramtron, }, +#endif +}; +#define IDCODE_LEN (IDCODE_CONT_LEN + IDCODE_PART_LEN) + +struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, +		unsigned int max_hz, unsigned int spi_mode) +{ +	struct spi_slave *spi; +	struct spi_flash *flash = NULL; +	int ret, i, shift; +	u8 idcode[IDCODE_LEN], *idp; + +	spi = spi_setup_slave(bus, cs, max_hz, spi_mode); +	if (!spi) { +		printf("SF: Failed to set up slave\n"); +		return NULL; +	} + +	ret = spi_claim_bus(spi); +	if (ret) { +		debug("SF: Failed to claim SPI bus: %d\n", ret); +		goto err_claim_bus; +	} + +	/* Read the ID codes */ +	ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode)); +	if (ret) +		goto err_read_id; + +#ifdef DEBUG +	printf("SF: Got idcodes\n"); +	print_buffer(0, idcode, 1, sizeof(idcode), 0); +#endif + +	/* count the number of continuation bytes */ +	for (shift = 0, idp = idcode; +	     shift < IDCODE_CONT_LEN && *idp == 0x7f; +	     ++shift, ++idp) +		continue; + +	/* search the table for matches in shift and id */ +	for (i = 0; i < ARRAY_SIZE(flashes); ++i) +		if (flashes[i].shift == shift && flashes[i].idcode == *idp) { +			/* we have a match, call probe */ +			flash = flashes[i].probe(spi, idp); +			if (flash) +				break; +		} + +	if (!flash) { +		printf("SF: Unsupported manufacturer %02x\n", *idp); +		goto err_manufacturer_probe; +	} + +	printf("SF: Detected %s with total size ", flash->name); +	print_size(flash->size, ""); +	puts("\n"); + +	spi_release_bus(spi); + +	return flash; + +err_manufacturer_probe: +err_read_id: +	spi_release_bus(spi); +err_claim_bus: +	spi_free_slave(spi); +	return NULL; +} + +void spi_flash_free(struct spi_flash *flash) +{ +	spi_free_slave(flash->spi); +	free(flash); +} diff --git a/drivers/mtd/spi/sf.c b/drivers/mtd/spi/sf.c new file mode 100644 index 000000000..ddbdda0dc --- /dev/null +++ b/drivers/mtd/spi/sf.c @@ -0,0 +1,54 @@ +/* + * SPI flash interface + * + * Copyright (C) 2008 Atmel Corporation + * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <spi.h> + +static int spi_flash_read_write(struct spi_slave *spi, +				const u8 *cmd, size_t cmd_len, +				const u8 *data_out, u8 *data_in, +				size_t data_len) +{ +	unsigned long flags = SPI_XFER_BEGIN; +	int ret; + +	if (data_len == 0) +		flags |= SPI_XFER_END; + +	ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); +	if (ret) { +		debug("SF: Failed to send command (%zu bytes): %d\n", +		      cmd_len, ret); +	} else if (data_len != 0) { +		ret = spi_xfer(spi, data_len * 8, data_out, data_in, +					SPI_XFER_END); +		if (ret) +			debug("SF: Failed to transfer %zu bytes of data: %d\n", +			      data_len, ret); +	} + +	return ret; +} + +int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, +		size_t cmd_len, void *data, size_t data_len) +{ +	return spi_flash_read_write(spi, cmd, cmd_len, NULL, data, data_len); +} + +int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len) +{ +	return spi_flash_cmd_read(spi, &cmd, 1, response, len); +} + +int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, +		const void *data, size_t data_len) +{ +	return spi_flash_read_write(spi, cmd, cmd_len, data, NULL, data_len); +} diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/sf_internal.h index af1afa96c..12d02f9e4 100644 --- a/drivers/mtd/spi/spi_flash_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -2,42 +2,43 @@   * SPI flash internal definitions   *   * Copyright (C) 2008 Atmel Corporation + * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. + * + * Licensed under the GPL-2 or later.   */ -/* Common parameters -- kind of high, but they should only occur when there - * is a problem (and well your system already is broken), so err on the side - * of caution in case we're dealing with slower SPI buses and/or processors. - */ -#define SPI_FLASH_PROG_TIMEOUT		(2 * CONFIG_SYS_HZ) -#define SPI_FLASH_PAGE_ERASE_TIMEOUT	(5 * CONFIG_SYS_HZ) -#define SPI_FLASH_SECTOR_ERASE_TIMEOUT	(10 * CONFIG_SYS_HZ) +#ifndef _SPI_FLASH_INTERNAL_H_ +#define _SPI_FLASH_INTERNAL_H_ -/* Common commands */ -#define CMD_READ_ID			0x9f +#define SPI_FLASH_16MB_BOUN		0x1000000 -#define CMD_READ_ARRAY_SLOW		0x03 -#define CMD_READ_ARRAY_FAST		0x0b +/* SECT flags */ +#define SECT_4K				(1 << 1) +#define SECT_32K			(1 << 2) +#define E_FSR				(1 << 3) + +/* Erase commands */ +#define CMD_ERASE_4K			0x20 +#define CMD_ERASE_32K			0x52 +#define CMD_ERASE_CHIP			0xc7 +#define CMD_ERASE_64K			0xd8 +/* Write commands */  #define CMD_WRITE_STATUS		0x01  #define CMD_PAGE_PROGRAM		0x02  #define CMD_WRITE_DISABLE		0x04  #define CMD_READ_STATUS			0x05 -#define CMD_FLAG_STATUS			0x70  #define CMD_WRITE_ENABLE		0x06 -#define CMD_ERASE_4K			0x20 -#define CMD_ERASE_32K			0x52 -#define CMD_ERASE_64K			0xd8 -#define CMD_ERASE_CHIP			0xc7 - -#define SPI_FLASH_16MB_BOUN		0x1000000 +#define CMD_READ_CONFIG			0x35 +#define CMD_FLAG_STATUS			0x70 -/* Manufacture ID's */ -#define SPI_FLASH_SPANSION_IDCODE0	0x01 -#define SPI_FLASH_STMICRO_IDCODE0	0x20 -#define SPI_FLASH_WINBOND_IDCODE0	0xef +/* Read commands */ +#define CMD_READ_ARRAY_SLOW		0x03 +#define CMD_READ_ARRAY_FAST		0x0b +#define CMD_READ_ID			0x9f -#ifdef CONFIG_SPI_FLASH_BAR  /* Bank addr access commands */ +#ifdef CONFIG_SPI_FLASH_BAR  # define CMD_BANKADDR_BRWR		0x17  # define CMD_BANKADDR_BRRD		0x16  # define CMD_EXTNADDR_WREAR		0xC5 @@ -48,6 +49,21 @@  #define STATUS_WIP			0x01  #define STATUS_PEC			0x80 +/* Flash timeout values */ +#define SPI_FLASH_PROG_TIMEOUT		(2 * CONFIG_SYS_HZ) +#define SPI_FLASH_PAGE_ERASE_TIMEOUT	(5 * CONFIG_SYS_HZ) +#define SPI_FLASH_SECTOR_ERASE_TIMEOUT	(10 * CONFIG_SYS_HZ) + +/* SST specific */ +#ifdef CONFIG_SPI_FLASH_SST +# define SST_WP			0x01	/* Supports AAI word program */ +# define CMD_SST_BP		0x02    /* Byte Program */ +# define CMD_SST_AAI_WP		0xAD	/* Auto Address Incr Word Program */ + +int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, +		const void *buf); +#endif +  /* Send a single-byte command to the device and read the response */  int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len); @@ -58,9 +74,6 @@ int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len);  int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd,  		size_t cmd_len, void *data, size_t data_len); -int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset, -		size_t len, void *data); -  /*   * Send a multi-byte command to the device followed by (optional)   * data. Used for programming the flash array, etc. @@ -68,46 +81,34 @@ int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,  int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,  		const void *data, size_t data_len); -/* - * Write the requested data out breaking it up into multiple write - * commands as needed per the write size. - */ -int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset, -		size_t len, const void *buf); -/* - * Enable writing on the SPI flash. - */ +/* Flash erase(sectors) operation, support all possible erase commands */ +int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len); + +/* Program the status register */ +int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr); + +/* Set quad enbale bit */ +int spi_flash_set_qeb(struct spi_flash *flash); + +/* Enable writing on the SPI flash */  static inline int spi_flash_cmd_write_enable(struct spi_flash *flash)  {  	return spi_flash_cmd(flash->spi, CMD_WRITE_ENABLE, NULL, 0);  } -/* - * Disable writing on the SPI flash. - */ +/* Disable writing on the SPI flash */  static inline int spi_flash_cmd_write_disable(struct spi_flash *flash)  {  	return spi_flash_cmd(flash->spi, CMD_WRITE_DISABLE, NULL, 0);  } -/* Program the status register. */ -int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr); - -#ifdef CONFIG_SPI_FLASH_BAR -/* Program the bank address register */ -int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel); - -/* Configure the BAR - discover the bank cmds */ -int spi_flash_bank_config(struct spi_flash *flash, u8 idcode0); -#endif -  /* - * Same as spi_flash_cmd_read() except it also claims/releases the SPI - * bus. Used as common part of the ->read() operation. + * Send the read status command to the device and wait for the wip + * (write-in-progress) bit to clear itself.   */ -int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, -		size_t cmd_len, void *data, size_t data_len); +int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout); +  /*   * Used for spi_flash write operation   * - SPI claim @@ -120,21 +121,22 @@ int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,  		size_t cmd_len, const void *buf, size_t buf_len);  /* - * Send the read status command to the device and wait for the wip - * (write-in-progress) bit to clear itself. + * Flash write operation, support all possible write commands. + * Write the requested data out breaking it up into multiple write + * commands as needed per the write size.   */ -int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout); +int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, +		size_t len, const void *buf); -/* Erase sectors. */ -int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len); +/* + * Same as spi_flash_cmd_read() except it also claims/releases the SPI + * bus. Used as common part of the ->read() operation. + */ +int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, +		size_t cmd_len, void *data, size_t data_len); + +/* Flash read operation, support all possible read commands */ +int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, +		size_t len, void *data); -/* Manufacturer-specific probe functions */ -struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode); -struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode); -struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode); -struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode); -struct spi_flash *spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode); -struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 *idcode); -struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode); -struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, u8 *idcode); -struct spi_flash *spi_flash_probe_gigadevice(struct spi_slave *spi, u8 *idcode); +#endif /* _SPI_FLASH_INTERNAL_H_ */ diff --git a/drivers/mtd/spi/sf_ops.c b/drivers/mtd/spi/sf_ops.c new file mode 100644 index 000000000..2396e2272 --- /dev/null +++ b/drivers/mtd/spi/sf_ops.c @@ -0,0 +1,405 @@ +/* + * SPI flash operations + * + * Copyright (C) 2008 Atmel Corporation + * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik + * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <spi.h> +#include <spi_flash.h> +#include <watchdog.h> + +#include "sf_internal.h" + +static void spi_flash_addr(u32 addr, u8 *cmd) +{ +	/* cmd[0] is actual command */ +	cmd[1] = addr >> 16; +	cmd[2] = addr >> 8; +	cmd[3] = addr >> 0; +} + +int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr) +{ +	u8 cmd; +	int ret; + +	cmd = CMD_WRITE_STATUS; +	ret = spi_flash_write_common(flash, &cmd, 1, &sr, 1); +	if (ret < 0) { +		debug("SF: fail to write status register\n"); +		return ret; +	} + +	return 0; +} + +#ifdef CONFIG_SPI_FLASH_BAR +static int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel) +{ +	u8 cmd; +	int ret; + +	if (flash->bank_curr == bank_sel) { +		debug("SF: not require to enable bank%d\n", bank_sel); +		return 0; +	} + +	cmd = flash->bank_write_cmd; +	ret = spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1); +	if (ret < 0) { +		debug("SF: fail to write bank register\n"); +		return ret; +	} +	flash->bank_curr = bank_sel; + +	return 0; +} +#endif + +int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ +	struct spi_slave *spi = flash->spi; +	unsigned long timebase; +	int ret; +	u8 status; +	u8 check_status = 0x0; +	u8 poll_bit = STATUS_WIP; +	u8 cmd = flash->poll_cmd; + +	if (cmd == CMD_FLAG_STATUS) { +		poll_bit = STATUS_PEC; +		check_status = poll_bit; +	} + +	ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); +	if (ret) { +		debug("SF: fail to read %s status register\n", +		      cmd == CMD_READ_STATUS ? "read" : "flag"); +		return ret; +	} + +	timebase = get_timer(0); +	do { +		WATCHDOG_RESET(); + +		ret = spi_xfer(spi, 8, NULL, &status, 0); +		if (ret) +			return -1; + +		if ((status & poll_bit) == check_status) +			break; + +	} while (get_timer(timebase) < timeout); + +	spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + +	if ((status & poll_bit) == check_status) +		return 0; + +	/* Timed out */ +	debug("SF: time out!\n"); +	return -1; +} + +int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, +		size_t cmd_len, const void *buf, size_t buf_len) +{ +	struct spi_slave *spi = flash->spi; +	unsigned long timeout = SPI_FLASH_PROG_TIMEOUT; +	int ret; + +	if (buf == NULL) +		timeout = SPI_FLASH_PAGE_ERASE_TIMEOUT; + +	ret = spi_claim_bus(flash->spi); +	if (ret) { +		debug("SF: unable to claim SPI bus\n"); +		return ret; +	} + +	ret = spi_flash_cmd_write_enable(flash); +	if (ret < 0) { +		debug("SF: enabling write failed\n"); +		return ret; +	} + +	ret = spi_flash_cmd_write(spi, cmd, cmd_len, buf, buf_len); +	if (ret < 0) { +		debug("SF: write cmd failed\n"); +		return ret; +	} + +	ret = spi_flash_cmd_wait_ready(flash, timeout); +	if (ret < 0) { +		debug("SF: write %s timed out\n", +		      timeout == SPI_FLASH_PROG_TIMEOUT ? +			"program" : "page erase"); +		return ret; +	} + +	spi_release_bus(spi); + +	return ret; +} + +int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) +{ +	u32 erase_size; +	u8 cmd[4]; +	int ret = -1; + +	erase_size = flash->erase_size; +	if (offset % erase_size || len % erase_size) { +		debug("SF: Erase offset/length not multiple of erase size\n"); +		return -1; +	} + +	cmd[0] = flash->erase_cmd; +	while (len) { +#ifdef CONFIG_SPI_FLASH_BAR +		u8 bank_sel; + +		bank_sel = offset / SPI_FLASH_16MB_BOUN; + +		ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); +		if (ret) { +			debug("SF: fail to set bank%d\n", bank_sel); +			return ret; +		} +#endif +		spi_flash_addr(offset, cmd); + +		debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], +		      cmd[2], cmd[3], offset); + +		ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0); +		if (ret < 0) { +			debug("SF: erase failed\n"); +			break; +		} + +		offset += erase_size; +		len -= erase_size; +	} + +	return ret; +} + +int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, +		size_t len, const void *buf) +{ +	unsigned long byte_addr, page_size; +	size_t chunk_len, actual; +	u8 cmd[4]; +	int ret = -1; + +	page_size = flash->page_size; + +	cmd[0] = CMD_PAGE_PROGRAM; +	for (actual = 0; actual < len; actual += chunk_len) { +#ifdef CONFIG_SPI_FLASH_BAR +		u8 bank_sel; + +		bank_sel = offset / SPI_FLASH_16MB_BOUN; + +		ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); +		if (ret) { +			debug("SF: fail to set bank%d\n", bank_sel); +			return ret; +		} +#endif +		byte_addr = offset % page_size; +		chunk_len = min(len - actual, page_size - byte_addr); + +		if (flash->spi->max_write_size) +			chunk_len = min(chunk_len, flash->spi->max_write_size); + +		spi_flash_addr(offset, cmd); + +		debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n", +		      buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); + +		ret = spi_flash_write_common(flash, cmd, sizeof(cmd), +					buf + actual, chunk_len); +		if (ret < 0) { +			debug("SF: write failed\n"); +			break; +		} + +		offset += chunk_len; +	} + +	return ret; +} + +int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, +		size_t cmd_len, void *data, size_t data_len) +{ +	struct spi_slave *spi = flash->spi; +	int ret; + +	ret = spi_claim_bus(flash->spi); +	if (ret) { +		debug("SF: unable to claim SPI bus\n"); +		return ret; +	} + +	ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len); +	if (ret < 0) { +		debug("SF: read cmd failed\n"); +		return ret; +	} + +	spi_release_bus(spi); + +	return ret; +} + +int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, +		size_t len, void *data) +{ +	u8 cmd[5], bank_sel = 0; +	u32 remain_len, read_len; +	int ret = -1; + +	/* Handle memory-mapped SPI */ +	if (flash->memory_map) { +		spi_xfer(flash->spi, 0, NULL, NULL, SPI_XFER_MMAP); +		memcpy(data, flash->memory_map + offset, len); +		spi_xfer(flash->spi, 0, NULL, NULL, SPI_XFER_MMAP_END); +		return 0; +	} + +	cmd[0] = CMD_READ_ARRAY_FAST; +	cmd[4] = 0x00; + +	while (len) { +#ifdef CONFIG_SPI_FLASH_BAR +		bank_sel = offset / SPI_FLASH_16MB_BOUN; + +		ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); +		if (ret) { +			debug("SF: fail to set bank%d\n", bank_sel); +			return ret; +		} +#endif +		remain_len = (SPI_FLASH_16MB_BOUN * (bank_sel + 1) - offset); +		if (len < remain_len) +			read_len = len; +		else +			read_len = remain_len; + +		spi_flash_addr(offset, cmd); + +		ret = spi_flash_read_common(flash, cmd, sizeof(cmd), +							data, read_len); +		if (ret < 0) { +			debug("SF: read failed\n"); +			break; +		} + +		offset += read_len; +		len -= read_len; +		data += read_len; +	} + +	return ret; +} + +#ifdef CONFIG_SPI_FLASH_SST +static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf) +{ +	int ret; +	u8 cmd[4] = { +		CMD_SST_BP, +		offset >> 16, +		offset >> 8, +		offset, +	}; + +	debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", +	      spi_w8r8(flash->spi, CMD_READ_STATUS), buf, cmd[0], offset); + +	ret = spi_flash_cmd_write_enable(flash); +	if (ret) +		return ret; + +	ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd), buf, 1); +	if (ret) +		return ret; + +	return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); +} + +int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, +		const void *buf) +{ +	size_t actual, cmd_len; +	int ret; +	u8 cmd[4]; + +	ret = spi_claim_bus(flash->spi); +	if (ret) { +		debug("SF: Unable to claim SPI bus\n"); +		return ret; +	} + +	/* If the data is not word aligned, write out leading single byte */ +	actual = offset % 2; +	if (actual) { +		ret = sst_byte_write(flash, offset, buf); +		if (ret) +			goto done; +	} +	offset += actual; + +	ret = spi_flash_cmd_write_enable(flash); +	if (ret) +		goto done; + +	cmd_len = 4; +	cmd[0] = CMD_SST_AAI_WP; +	cmd[1] = offset >> 16; +	cmd[2] = offset >> 8; +	cmd[3] = offset; + +	for (; actual < len - 1; actual += 2) { +		debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", +		      spi_w8r8(flash->spi, CMD_READ_STATUS), buf + actual, +		      cmd[0], offset); + +		ret = spi_flash_cmd_write(flash->spi, cmd, cmd_len, +					buf + actual, 2); +		if (ret) { +			debug("SF: sst word program failed\n"); +			break; +		} + +		ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); +		if (ret) +			break; + +		cmd_len = 1; +		offset += 2; +	} + +	if (!ret) +		ret = spi_flash_cmd_write_disable(flash); + +	/* If there is a single trailing byte, write it out */ +	if (!ret && actual != len) +		ret = sst_byte_write(flash, offset, buf + actual); + + done: +	debug("SF: sst: program %s %zu bytes @ 0x%zx\n", +	      ret ? "failure" : "success", len, offset - actual); + +	spi_release_bus(flash->spi); +	return ret; +} +#endif diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c new file mode 100644 index 000000000..4251b1be1 --- /dev/null +++ b/drivers/mtd/spi/sf_probe.c @@ -0,0 +1,363 @@ +/* + * SPI flash probing + * + * Copyright (C) 2008 Atmel Corporation + * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik + * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <fdtdec.h> +#include <malloc.h> +#include <spi.h> +#include <spi_flash.h> + +#include "sf_internal.h" + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct spi_flash_params - SPI/QSPI flash device params structure + * + * @name:		Device name ([MANUFLETTER][DEVTYPE][DENSITY][EXTRAINFO]) + * @jedec:		Device jedec ID (0x[1byte_manuf_id][2byte_dev_id]) + * @ext_jedec:		Device ext_jedec ID + * @sector_size:	Sector size of this device + * @nr_sectors:	No.of sectors on this device + * @flags:		Importent param, for flash specific behaviour + */ +struct spi_flash_params { +	const char *name; +	u32 jedec; +	u16 ext_jedec; +	u32 sector_size; +	u32 nr_sectors; +	u16 flags; +}; + +static const struct spi_flash_params spi_flash_params_table[] = { +#ifdef CONFIG_SPI_FLASH_ATMEL		/* ATMEL */ +	{"AT45DB011D",	   0x1f2200, 0x0,	64 * 1024,     4,	       SECT_4K}, +	{"AT45DB021D",	   0x1f2300, 0x0,	64 * 1024,     8,	       SECT_4K}, +	{"AT45DB041D",	   0x1f2400, 0x0,	64 * 1024,     8,	       SECT_4K}, +	{"AT45DB081D",	   0x1f2500, 0x0,	64 * 1024,    16,	       SECT_4K}, +	{"AT45DB161D",	   0x1f2600, 0x0,	64 * 1024,    32,	       SECT_4K}, +	{"AT45DB321D",	   0x1f2700, 0x0,	64 * 1024,    64,	       SECT_4K}, +	{"AT45DB641D",	   0x1f2800, 0x0,	64 * 1024,   128,	       SECT_4K}, +#endif +#ifdef CONFIG_SPI_FLASH_EON		/* EON */ +	{"EN25Q32B",	   0x1c3016, 0x0,	64 * 1024,    64,	             0}, +	{"EN25Q64",	   0x1c3017, 0x0,	64 * 1024,   128,	       SECT_4K}, +	{"EN25Q128B",	   0x1c3018, 0x0,       64 * 1024,   256,	             0}, +	{"EN25S64",	   0x1c3817, 0x0,	64 * 1024,   128,		     0}, +#endif +#ifdef CONFIG_SPI_FLASH_GIGADEVICE	/* GIGADEVICE */ +	{"GD25Q64B",	   0xc84017, 0x0,	64 * 1024,   128,	       SECT_4K}, +	{"GD25LQ32",	   0xc86016, 0x0,	64 * 1024,    64,	       SECT_4K}, +#endif +#ifdef CONFIG_SPI_FLASH_MACRONIX	/* MACRONIX */ +	{"MX25L4005",	   0xc22013, 0x0,	64 * 1024,     8,	             0}, +	{"MX25L8005",	   0xc22014, 0x0,	64 * 1024,    16,	             0}, +	{"MX25L1605D",	   0xc22015, 0x0,	64 * 1024,    32,	             0}, +	{"MX25L3205D",	   0xc22016, 0x0,	64 * 1024,    64,	             0}, +	{"MX25L6405D",	   0xc22017, 0x0,	64 * 1024,   128,	             0}, +	{"MX25L12805",	   0xc22018, 0x0,	64 * 1024,   256,	             0}, +	{"MX25L25635F",	   0xc22019, 0x0,	64 * 1024,   512,	             0}, +	{"MX25L51235F",	   0xc2201A, 0x0,	64 * 1024,  1024,	             0}, +	{"MX25L12855E",	   0xc22618, 0x0,	64 * 1024,   256,	             0}, +#endif +#ifdef CONFIG_SPI_FLASH_SPANSION	/* SPANSION */ +	{"S25FL008A",	   0x010213, 0x0,	64 * 1024,    16,	             0}, +	{"S25FL016A",	   0x010214, 0x0,	64 * 1024,    32,	             0}, +	{"S25FL032A",	   0x010215, 0x0,	64 * 1024,    64,	             0}, +	{"S25FL064A",	   0x010216, 0x0,	64 * 1024,   128,	             0}, +	{"S25FL128P_256K", 0x012018, 0x0300,   256 * 1024,    64,	             0}, +	{"S25FL128P_64K",  0x012018, 0x0301,    64 * 1024,   256,	             0}, +	{"S25FL032P",	   0x010215, 0x4d00,    64 * 1024,    64,	             0}, +	{"S25FL064P",	   0x010216, 0x4d00,    64 * 1024,   128,	             0}, +	{"S25FL128S_64K",  0x012018, 0x4d01,    64 * 1024,   256,		     0}, +	{"S25FL256S_256K", 0x010219, 0x4d00,    64 * 1024,   512,	             0}, +	{"S25FL256S_64K",  0x010219, 0x4d01,    64 * 1024,   512,	             0}, +	{"S25FL512S_256K", 0x010220, 0x4d00,    64 * 1024,  1024,	             0}, +	{"S25FL512S_64K",  0x010220, 0x4d01,    64 * 1024,  1024,	             0}, +#endif +#ifdef CONFIG_SPI_FLASH_STMICRO		/* STMICRO */ +	{"M25P10",	   0x202011, 0x0,       32 * 1024,     4,	             0}, +	{"M25P20",	   0x202012, 0x0,       64 * 1024,     4,	             0}, +	{"M25P40",	   0x202013, 0x0,       64 * 1024,     8,	             0}, +	{"M25P80",	   0x202014, 0x0,       64 * 1024,    16,	             0}, +	{"M25P16",	   0x202015, 0x0,       64 * 1024,    32,	             0}, +	{"M25P32",	   0x202016, 0x0,       64 * 1024,    64,	             0}, +	{"M25P64",	   0x202017, 0x0,       64 * 1024,   128,	             0}, +	{"M25P128",	   0x202018, 0x0,      256 * 1024,    64,	             0}, +	{"N25Q32",	   0x20ba16, 0x0,       64 * 1024,    64,	       SECT_4K}, +	{"N25Q32A",	   0x20bb16, 0x0,       64 * 1024,    64,	       SECT_4K}, +	{"N25Q64",	   0x20ba17, 0x0,       64 * 1024,   128,	       SECT_4K}, +	{"N25Q64A",	   0x20bb17, 0x0,       64 * 1024,   128,	       SECT_4K}, +	{"N25Q128",	   0x20ba18, 0x0,       64 * 1024,   256,	       SECT_4K}, +	{"N25Q128A",	   0x20bb18, 0x0,       64 * 1024,   256,	       SECT_4K}, +	{"N25Q256",	   0x20ba19, 0x0,       64 * 1024,   512,	       SECT_4K}, +	{"N25Q256A",	   0x20bb19, 0x0,       64 * 1024,   512,	       SECT_4K}, +	{"N25Q512",	   0x20ba20, 0x0,       64 * 1024,  1024,      E_FSR | SECT_4K}, +	{"N25Q512A",	   0x20bb20, 0x0,       64 * 1024,  1024,      E_FSR | SECT_4K}, +	{"N25Q1024",	   0x20ba21, 0x0,       64 * 1024,  2048,      E_FSR | SECT_4K}, +	{"N25Q1024A",	   0x20bb21, 0x0,       64 * 1024,  2048,      E_FSR | SECT_4K}, +#endif +#ifdef CONFIG_SPI_FLASH_SST		/* SST */ +	{"SST25VF040B",	   0xbf258d, 0x0,	64 * 1024,     8,     SECT_4K | SST_WP}, +	{"SST25VF080B",	   0xbf258e, 0x0,	64 * 1024,    16,     SECT_4K | SST_WP}, +	{"SST25VF016B",	   0xbf2541, 0x0,	64 * 1024,    32,     SECT_4K | SST_WP}, +	{"SST25VF032B",	   0xbf254a, 0x0,	64 * 1024,    64,     SECT_4K | SST_WP}, +	{"SST25VF064C",	   0xbf254b, 0x0,	64 * 1024,   128,	       SECT_4K}, +	{"SST25WF512",	   0xbf2501, 0x0,	64 * 1024,     1,     SECT_4K | SST_WP}, +	{"SST25WF010",	   0xbf2502, 0x0,	64 * 1024,     2,     SECT_4K | SST_WP}, +	{"SST25WF020",	   0xbf2503, 0x0,	64 * 1024,     4,     SECT_4K | SST_WP}, +	{"SST25WF040",	   0xbf2504, 0x0,	64 * 1024,     8,     SECT_4K | SST_WP}, +	{"SST25WF080",	   0xbf2505, 0x0,	64 * 1024,    16,     SECT_4K | SST_WP}, +#endif +#ifdef CONFIG_SPI_FLASH_WINBOND		/* WINBOND */ +	{"W25P80",	   0xef2014, 0x0,	64 * 1024,    16,		    0}, +	{"W25P16",	   0xef2015, 0x0,	64 * 1024,    32,		    0}, +	{"W25P32",	   0xef2016, 0x0,	64 * 1024,    64,		    0}, +	{"W25X40",	   0xef3013, 0x0,	64 * 1024,     8,	      SECT_4K}, +	{"W25X16",	   0xef3015, 0x0,	64 * 1024,    32,	      SECT_4K}, +	{"W25X32",	   0xef3016, 0x0,	64 * 1024,    64,	      SECT_4K}, +	{"W25X64",	   0xef3017, 0x0,	64 * 1024,   128,	      SECT_4K}, +	{"W25Q80BL",	   0xef4014, 0x0,	64 * 1024,    16,	      SECT_4K}, +	{"W25Q16CL",	   0xef4015, 0x0,	64 * 1024,    32,	      SECT_4K}, +	{"W25Q32BV",	   0xef4016, 0x0,	64 * 1024,    64,	      SECT_4K}, +	{"W25Q64CV",	   0xef4017, 0x0,	64 * 1024,   128,	      SECT_4K}, +	{"W25Q128BV",	   0xef4018, 0x0,	64 * 1024,   256,	      SECT_4K}, +	{"W25Q256",	   0xef4019, 0x0,	64 * 1024,   512,	      SECT_4K}, +	{"W25Q80BW",	   0xef5014, 0x0,	64 * 1024,    16,	      SECT_4K}, +	{"W25Q16DW",	   0xef6015, 0x0,	64 * 1024,    32,	      SECT_4K}, +	{"W25Q32DW",	   0xef6016, 0x0,	64 * 1024,    64,	      SECT_4K}, +	{"W25Q64DW",	   0xef6017, 0x0,	64 * 1024,   128,	      SECT_4K}, +	{"W25Q128FW",	   0xef6018, 0x0,	64 * 1024,   256,	      SECT_4K}, +#endif +	/* +	 * Note: +	 * Below paired flash devices has similar spi_flash_params params. +	 * (S25FL129P_64K, S25FL128S_64K) +	 * (W25Q80BL, W25Q80BV) +	 * (W25Q16CL, W25Q16DV) +	 * (W25Q32BV, W25Q32FV_SPI) +	 * (W25Q64CV, W25Q64FV_SPI) +	 * (W25Q128BV, W25Q128FV_SPI) +	 * (W25Q32DW, W25Q32FV_QPI) +	 * (W25Q64DW, W25Q64FV_QPI) +	 * (W25Q128FW, W25Q128FV_QPI) +	 */ +}; + +static struct spi_flash *spi_flash_validate_params(struct spi_slave *spi, +		u8 *idcode) +{ +	const struct spi_flash_params *params; +	struct spi_flash *flash; +	int i; +	u16 jedec = idcode[1] << 8 | idcode[2]; +	u16 ext_jedec = idcode[3] << 8 | idcode[4]; + +	/* Get the flash id (jedec = manuf_id + dev_id, ext_jedec) */ +	for (i = 0; i < ARRAY_SIZE(spi_flash_params_table); i++) { +		params = &spi_flash_params_table[i]; +		if ((params->jedec >> 16) == idcode[0]) { +			if ((params->jedec & 0xFFFF) == jedec) { +				if (params->ext_jedec == 0) +					break; +				else if (params->ext_jedec == ext_jedec) +					break; +			} +		} +	} + +	if (i == ARRAY_SIZE(spi_flash_params_table)) { +		printf("SF: Unsupported flash IDs: "); +		printf("manuf %02x, jedec %04x, ext_jedec %04x\n", +		       idcode[0], jedec, ext_jedec); +		return NULL; +	} + +	flash = malloc(sizeof(*flash)); +	if (!flash) { +		debug("SF: Failed to allocate spi_flash\n"); +		return NULL; +	} +	memset(flash, '\0', sizeof(*flash)); + +	flash->spi = spi; +	flash->name = params->name; +	flash->memory_map = spi->memory_map; + +	/* Assign spi_flash ops */ +	flash->write = spi_flash_cmd_write_ops; +#ifdef CONFIG_SPI_FLASH_SST +	if (params->flags & SST_WP) +		flash->write = sst_write_wp; +#endif +	flash->erase = spi_flash_cmd_erase_ops; +	flash->read = spi_flash_cmd_read_ops; + +	/* Compute the flash size */ +	flash->page_size = (ext_jedec == 0x4d00) ? 512 : 256; +	flash->sector_size = params->sector_size; +	flash->size = flash->sector_size * params->nr_sectors; + +	/* Compute erase sector and command */ +	if (params->flags & SECT_4K) { +		flash->erase_cmd = CMD_ERASE_4K; +		flash->erase_size = 4096; +	} else if (params->flags & SECT_32K) { +		flash->erase_cmd = CMD_ERASE_32K; +		flash->erase_size = 32768; +	} else { +		flash->erase_cmd = CMD_ERASE_64K; +		flash->erase_size = flash->sector_size; +	} + +	/* Poll cmd seclection */ +	flash->poll_cmd = CMD_READ_STATUS; +#ifdef CONFIG_SPI_FLASH_STMICRO +	if (params->flags & E_FSR) +		flash->poll_cmd = CMD_FLAG_STATUS; +#endif + +	/* Configure the BAR - discover bank cmds and read current bank */ +#ifdef CONFIG_SPI_FLASH_BAR +	u8 curr_bank = 0; +	if (flash->size > SPI_FLASH_16MB_BOUN) { +		flash->bank_read_cmd = (idcode[0] == 0x01) ? +					CMD_BANKADDR_BRRD : CMD_EXTNADDR_RDEAR; +		flash->bank_write_cmd = (idcode[0] == 0x01) ? +					CMD_BANKADDR_BRWR : CMD_EXTNADDR_WREAR; + +		if (spi_flash_read_common(flash, &flash->bank_read_cmd, 1, +					  &curr_bank, 1)) { +			debug("SF: fail to read bank addr register\n"); +			return NULL; +		} +		flash->bank_curr = curr_bank; +	} else { +		flash->bank_curr = curr_bank; +	} +#endif + +	/* Flash powers up read-only, so clear BP# bits */ +#if defined(CONFIG_SPI_FLASH_ATMEL) || \ +	defined(CONFIG_SPI_FLASH_MACRONIX) || \ +	defined(CONFIG_SPI_FLASH_SST) +		spi_flash_cmd_write_status(flash, 0); +#endif + +	return flash; +} + +#ifdef CONFIG_OF_CONTROL +int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash) +{ +	fdt_addr_t addr; +	fdt_size_t size; +	int node; + +	/* If there is no node, do nothing */ +	node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH); +	if (node < 0) +		return 0; + +	addr = fdtdec_get_addr_size(blob, node, "memory-map", &size); +	if (addr == FDT_ADDR_T_NONE) { +		debug("%s: Cannot decode address\n", __func__); +		return 0; +	} + +	if (flash->size != size) { +		debug("%s: Memory map must cover entire device\n", __func__); +		return -1; +	} +	flash->memory_map = (void *)addr; + +	return 0; +} +#endif /* CONFIG_OF_CONTROL */ + +struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, +		unsigned int max_hz, unsigned int spi_mode) +{ +	struct spi_slave *spi; +	struct spi_flash *flash = NULL; +	u8 idcode[5]; +	int ret; + +	/* Setup spi_slave */ +	spi = spi_setup_slave(bus, cs, max_hz, spi_mode); +	if (!spi) { +		printf("SF: Failed to set up slave\n"); +		return NULL; +	} + +	/* Claim spi bus */ +	ret = spi_claim_bus(spi); +	if (ret) { +		debug("SF: Failed to claim SPI bus: %d\n", ret); +		goto err_claim_bus; +	} + +	/* Read the ID codes */ +	ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode)); +	if (ret) { +		printf("SF: Failed to get idcodes\n"); +		goto err_read_id; +	} + +#ifdef DEBUG +	printf("SF: Got idcodes\n"); +	print_buffer(0, idcode, 1, sizeof(idcode), 0); +#endif + +	/* Validate params from spi_flash_params table */ +	flash = spi_flash_validate_params(spi, idcode); +	if (!flash) +		goto err_read_id; + +#ifdef CONFIG_OF_CONTROL +	if (spi_flash_decode_fdt(gd->fdt_blob, flash)) { +		debug("SF: FDT decode error\n"); +		goto err_read_id; +	} +#endif +#ifndef CONFIG_SPL_BUILD +	printf("SF: Detected %s with page size ", flash->name); +	print_size(flash->page_size, ", erase size "); +	print_size(flash->erase_size, ", total "); +	print_size(flash->size, ""); +	if (flash->memory_map) +		printf(", mapped at %p", flash->memory_map); +	puts("\n"); +#endif +#ifndef CONFIG_SPI_FLASH_BAR +	if (flash->size > SPI_FLASH_16MB_BOUN) { +		puts("SF: Warning - Only lower 16MiB accessible,"); +		puts(" Full access #define CONFIG_SPI_FLASH_BAR\n"); +	} +#endif + +	/* Release spi bus */ +	spi_release_bus(spi); + +	return flash; + +err_read_id: +	spi_release_bus(spi); +err_claim_bus: +	spi_free_slave(spi); +	return NULL; +} + +void spi_flash_free(struct spi_flash *flash) +{ +	spi_free_slave(flash->spi); +	free(flash); +} diff --git a/drivers/mtd/spi/spansion.c b/drivers/mtd/spi/spansion.c deleted file mode 100644 index fa7ac8c93..000000000 --- a/drivers/mtd/spi/spansion.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2009 Freescale Semiconductor, Inc. - * - * Author: Mingkai Hu (Mingkai.hu@freescale.com) - * Based on stmicro.c by Wolfgang Denk (wd@denx.de), - * TsiChung Liew (Tsi-Chung.Liew@freescale.com), - * and  Jason McMullan (mcmullan@netapp.com) - * - * SPDX-License-Identifier:	GPL-2.0+ - */ - -#include <common.h> -#include <malloc.h> -#include <spi_flash.h> - -#include "spi_flash_internal.h" - -struct spansion_spi_flash_params { -	u16 idcode1; -	u16 idcode2; -	u16 pages_per_sector; -	u16 nr_sectors; -	const char *name; -}; - -static const struct spansion_spi_flash_params spansion_spi_flash_table[] = { -	{ -		.idcode1 = 0x0213, -		.idcode2 = 0, -		.pages_per_sector = 256, -		.nr_sectors = 16, -		.name = "S25FL008A", -	}, -	{ -		.idcode1 = 0x0214, -		.idcode2 = 0, -		.pages_per_sector = 256, -		.nr_sectors = 32, -		.name = "S25FL016A", -	}, -	{ -		.idcode1 = 0x0215, -		.idcode2 = 0, -		.pages_per_sector = 256, -		.nr_sectors = 64, -		.name = "S25FL032A", -	}, -	{ -		.idcode1 = 0x0216, -		.idcode2 = 0, -		.pages_per_sector = 256, -		.nr_sectors = 128, -		.name = "S25FL064A", -	}, -	{ -		.idcode1 = 0x2018, -		.idcode2 = 0x0301, -		.pages_per_sector = 256, -		.nr_sectors = 256, -		.name = "S25FL128P_64K", -	}, -	{ -		.idcode1 = 0x2018, -		.idcode2 = 0x0300, -		.pages_per_sector = 1024, -		.nr_sectors = 64, -		.name = "S25FL128P_256K", -	}, -	{ -		.idcode1 = 0x0215, -		.idcode2 = 0x4d00, -		.pages_per_sector = 256, -		.nr_sectors = 64, -		.name = "S25FL032P", -	}, -	{ -		.idcode1 = 0x0216, -		.idcode2 = 0x4d00, -		.pages_per_sector = 256, -		.nr_sectors = 128, -		.name = "S25FL064P", -	}, -	{ -		.idcode1 = 0x2018, -		.idcode2 = 0x4d01, -		.pages_per_sector = 256, -		.nr_sectors = 256, -		.name = "S25FL129P_64K/S25FL128S_64K", -	}, -	{ -		.idcode1 = 0x0219, -		.idcode2 = 0x4d01, -		.pages_per_sector = 256, -		.nr_sectors = 512, -		.name = "S25FL256S_64K", -	}, -	{ -		.idcode1 = 0x0220, -		.idcode2 = 0x4d01, -		.pages_per_sector = 256, -		.nr_sectors = 1024, -		.name = "S25FL512S_64K", -	}, -}; - -struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode) -{ -	const struct spansion_spi_flash_params *params; -	struct spi_flash *flash; -	unsigned int i; -	unsigned short jedec, ext_jedec; - -	jedec = idcode[1] << 8 | idcode[2]; -	ext_jedec = idcode[3] << 8 | idcode[4]; - -	for (i = 0; i < ARRAY_SIZE(spansion_spi_flash_table); i++) { -		params = &spansion_spi_flash_table[i]; -		if (params->idcode1 == jedec) { -			if (params->idcode2 == ext_jedec) -				break; -		} -	} - -	if (i == ARRAY_SIZE(spansion_spi_flash_table)) { -		debug("SF: Unsupported SPANSION ID %04x %04x\n", -		      jedec, ext_jedec); -		return NULL; -	} - -	flash = spi_flash_alloc_base(spi, params->name); -	if (!flash) { -		debug("SF: Failed to allocate memory\n"); -		return NULL; -	} - -	flash->page_size = 256; -	flash->sector_size = 256 * params->pages_per_sector; -	flash->size = flash->sector_size * params->nr_sectors; - -	return flash; -} diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c deleted file mode 100644 index 5d5055ff3..000000000 --- a/drivers/mtd/spi/spi_flash.c +++ /dev/null @@ -1,615 +0,0 @@ -/* - * SPI flash interface - * - * Copyright (C) 2008 Atmel Corporation - * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik - * - * Licensed under the GPL-2 or later. - */ - -#include <common.h> -#include <fdtdec.h> -#include <malloc.h> -#include <spi.h> -#include <spi_flash.h> -#include <watchdog.h> - -#include "spi_flash_internal.h" - -DECLARE_GLOBAL_DATA_PTR; - -static void spi_flash_addr(u32 addr, u8 *cmd) -{ -	/* cmd[0] is actual command */ -	cmd[1] = addr >> 16; -	cmd[2] = addr >> 8; -	cmd[3] = addr >> 0; -} - -static int spi_flash_read_write(struct spi_slave *spi, -				const u8 *cmd, size_t cmd_len, -				const u8 *data_out, u8 *data_in, -				size_t data_len) -{ -	unsigned long flags = SPI_XFER_BEGIN; -	int ret; - -	if (data_len == 0) -		flags |= SPI_XFER_END; - -	ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); -	if (ret) { -		debug("SF: Failed to send command (%zu bytes): %d\n", -		      cmd_len, ret); -	} else if (data_len != 0) { -		ret = spi_xfer(spi, data_len * 8, data_out, data_in, -					SPI_XFER_END); -		if (ret) -			debug("SF: Failed to transfer %zu bytes of data: %d\n", -			      data_len, ret); -	} - -	return ret; -} - -int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len) -{ -	return spi_flash_cmd_read(spi, &cmd, 1, response, len); -} - -int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, -		size_t cmd_len, void *data, size_t data_len) -{ -	return spi_flash_read_write(spi, cmd, cmd_len, NULL, data, data_len); -} - -int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, -		const void *data, size_t data_len) -{ -	return spi_flash_read_write(spi, cmd, cmd_len, data, NULL, data_len); -} - -int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout) -{ -	struct spi_slave *spi = flash->spi; -	unsigned long timebase; -	int ret; -	u8 status; -	u8 check_status = 0x0; -	u8 poll_bit = STATUS_WIP; -	u8 cmd = flash->poll_cmd; - -	if (cmd == CMD_FLAG_STATUS) { -		poll_bit = STATUS_PEC; -		check_status = poll_bit; -	} - -	ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); -	if (ret) { -		debug("SF: fail to read %s status register\n", -		      cmd == CMD_READ_STATUS ? "read" : "flag"); -		return ret; -	} - -	timebase = get_timer(0); -	do { -		WATCHDOG_RESET(); - -		ret = spi_xfer(spi, 8, NULL, &status, 0); -		if (ret) -			return -1; - -		if ((status & poll_bit) == check_status) -			break; - -	} while (get_timer(timebase) < timeout); - -	spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); - -	if ((status & poll_bit) == check_status) -		return 0; - -	/* Timed out */ -	debug("SF: time out!\n"); -	return -1; -} - -int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, -		size_t cmd_len, const void *buf, size_t buf_len) -{ -	struct spi_slave *spi = flash->spi; -	unsigned long timeout = SPI_FLASH_PROG_TIMEOUT; -	int ret; - -	if (buf == NULL) -		timeout = SPI_FLASH_PAGE_ERASE_TIMEOUT; - -	ret = spi_claim_bus(flash->spi); -	if (ret) { -		debug("SF: unable to claim SPI bus\n"); -		return ret; -	} - -	ret = spi_flash_cmd_write_enable(flash); -	if (ret < 0) { -		debug("SF: enabling write failed\n"); -		return ret; -	} - -	ret = spi_flash_cmd_write(spi, cmd, cmd_len, buf, buf_len); -	if (ret < 0) { -		debug("SF: write cmd failed\n"); -		return ret; -	} - -	ret = spi_flash_cmd_wait_ready(flash, timeout); -	if (ret < 0) { -		debug("SF: write %s timed out\n", -		      timeout == SPI_FLASH_PROG_TIMEOUT ? -			"program" : "page erase"); -		return ret; -	} - -	spi_release_bus(spi); - -	return ret; -} - -int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len) -{ -	u32 erase_size; -	u8 cmd[4]; -	int ret = -1; - -	erase_size = flash->sector_size; -	if (offset % erase_size || len % erase_size) { -		debug("SF: Erase offset/length not multiple of erase size\n"); -		return -1; -	} - -	if (erase_size == 4096) -		cmd[0] = CMD_ERASE_4K; -	else -		cmd[0] = CMD_ERASE_64K; - -	while (len) { -#ifdef CONFIG_SPI_FLASH_BAR -		u8 bank_sel; - -		bank_sel = offset / SPI_FLASH_16MB_BOUN; - -		ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); -		if (ret) { -			debug("SF: fail to set bank%d\n", bank_sel); -			return ret; -		} -#endif -		spi_flash_addr(offset, cmd); - -		debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], -		      cmd[2], cmd[3], offset); - -		ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0); -		if (ret < 0) { -			debug("SF: erase failed\n"); -			break; -		} - -		offset += erase_size; -		len -= erase_size; -	} - -	return ret; -} - -int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset, -		size_t len, const void *buf) -{ -	unsigned long byte_addr, page_size; -	size_t chunk_len, actual; -	u8 cmd[4]; -	int ret = -1; - -	page_size = flash->page_size; - -	cmd[0] = CMD_PAGE_PROGRAM; -	for (actual = 0; actual < len; actual += chunk_len) { -#ifdef CONFIG_SPI_FLASH_BAR -		u8 bank_sel; - -		bank_sel = offset / SPI_FLASH_16MB_BOUN; - -		ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); -		if (ret) { -			debug("SF: fail to set bank%d\n", bank_sel); -			return ret; -		} -#endif -		byte_addr = offset % page_size; -		chunk_len = min(len - actual, page_size - byte_addr); - -		if (flash->spi->max_write_size) -			chunk_len = min(chunk_len, flash->spi->max_write_size); - -		spi_flash_addr(offset, cmd); - -		debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n", -		      buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); - -		ret = spi_flash_write_common(flash, cmd, sizeof(cmd), -					buf + actual, chunk_len); -		if (ret < 0) { -			debug("SF: write failed\n"); -			break; -		} - -		offset += chunk_len; -	} - -	return ret; -} - -int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, -		size_t cmd_len, void *data, size_t data_len) -{ -	struct spi_slave *spi = flash->spi; -	int ret; - -	ret = spi_claim_bus(flash->spi); -	if (ret) { -		debug("SF: unable to claim SPI bus\n"); -		return ret; -	} - -	ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len); -	if (ret < 0) { -		debug("SF: read cmd failed\n"); -		return ret; -	} - -	spi_release_bus(spi); - -	return ret; -} - -int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset, -		size_t len, void *data) -{ -	u8 cmd[5], bank_sel = 0; -	u32 remain_len, read_len; -	int ret = -1; - -	/* Handle memory-mapped SPI */ -	if (flash->memory_map) { -		memcpy(data, flash->memory_map + offset, len); -		return 0; -	} - -	cmd[0] = CMD_READ_ARRAY_FAST; -	cmd[4] = 0x00; - -	while (len) { -#ifdef CONFIG_SPI_FLASH_BAR -		bank_sel = offset / SPI_FLASH_16MB_BOUN; - -		ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); -		if (ret) { -			debug("SF: fail to set bank%d\n", bank_sel); -			return ret; -		} -#endif -		remain_len = (SPI_FLASH_16MB_BOUN * (bank_sel + 1) - offset); -		if (len < remain_len) -			read_len = len; -		else -			read_len = remain_len; - -		spi_flash_addr(offset, cmd); - -		ret = spi_flash_read_common(flash, cmd, sizeof(cmd), -							data, read_len); -		if (ret < 0) { -			debug("SF: read failed\n"); -			break; -		} - -		offset += read_len; -		len -= read_len; -		data += read_len; -	} - -	return ret; -} - -int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr) -{ -	u8 cmd; -	int ret; - -	cmd = CMD_WRITE_STATUS; -	ret = spi_flash_write_common(flash, &cmd, 1, &sr, 1); -	if (ret < 0) { -		debug("SF: fail to write status register\n"); -		return ret; -	} - -	return 0; -} - -#ifdef CONFIG_SPI_FLASH_BAR -int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel) -{ -	u8 cmd; -	int ret; - -	if (flash->bank_curr == bank_sel) { -		debug("SF: not require to enable bank%d\n", bank_sel); -		return 0; -	} - -	cmd = flash->bank_write_cmd; -	ret = spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1); -	if (ret < 0) { -		debug("SF: fail to write bank register\n"); -		return ret; -	} -	flash->bank_curr = bank_sel; - -	return 0; -} - -int spi_flash_bank_config(struct spi_flash *flash, u8 idcode0) -{ -	u8 cmd; -	u8 curr_bank = 0; - -	/* discover bank cmds */ -	switch (idcode0) { -	case SPI_FLASH_SPANSION_IDCODE0: -		flash->bank_read_cmd = CMD_BANKADDR_BRRD; -		flash->bank_write_cmd = CMD_BANKADDR_BRWR; -		break; -	case SPI_FLASH_STMICRO_IDCODE0: -	case SPI_FLASH_WINBOND_IDCODE0: -		flash->bank_read_cmd = CMD_EXTNADDR_RDEAR; -		flash->bank_write_cmd = CMD_EXTNADDR_WREAR; -		break; -	default: -		printf("SF: Unsupported bank commands %02x\n", idcode0); -		return -1; -	} - -	/* read the bank reg - on which bank the flash is in currently */ -	cmd = flash->bank_read_cmd; -	if (flash->size > SPI_FLASH_16MB_BOUN) { -		if (spi_flash_read_common(flash, &cmd, 1, &curr_bank, 1)) { -			debug("SF: fail to read bank addr register\n"); -			return -1; -		} -		flash->bank_curr = curr_bank; -	} else { -		flash->bank_curr = curr_bank; -	} - -	return 0; -} -#endif - -#ifdef CONFIG_OF_CONTROL -int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash) -{ -	fdt_addr_t addr; -	fdt_size_t size; -	int node; - -	/* If there is no node, do nothing */ -	node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH); -	if (node < 0) -		return 0; - -	addr = fdtdec_get_addr_size(blob, node, "memory-map", &size); -	if (addr == FDT_ADDR_T_NONE) { -		debug("%s: Cannot decode address\n", __func__); -		return 0; -	} - -	if (flash->size != size) { -		debug("%s: Memory map must cover entire device\n", __func__); -		return -1; -	} -	flash->memory_map = (void *)addr; - -	return 0; -} -#endif /* CONFIG_OF_CONTROL */ - -/* - * The following table holds all device probe functions - * - * shift:  number of continuation bytes before the ID - * idcode: the expected IDCODE or 0xff for non JEDEC devices - * probe:  the function to call - * - * Non JEDEC devices should be ordered in the table such that - * the probe functions with best detection algorithms come first. - * - * Several matching entries are permitted, they will be tried - * in sequence until a probe function returns non NULL. - * - * IDCODE_CONT_LEN may be redefined if a device needs to declare a - * larger "shift" value.  IDCODE_PART_LEN generally shouldn't be - * changed.  This is the max number of bytes probe functions may - * examine when looking up part-specific identification info. - * - * Probe functions will be given the idcode buffer starting at their - * manu id byte (the "idcode" in the table below).  In other words, - * all of the continuation bytes will be skipped (the "shift" below). - */ -#define IDCODE_CONT_LEN 0 -#define IDCODE_PART_LEN 5 -static const struct { -	const u8 shift; -	const u8 idcode; -	struct spi_flash *(*probe) (struct spi_slave *spi, u8 *idcode); -} flashes[] = { -	/* Keep it sorted by define name */ -#ifdef CONFIG_SPI_FLASH_ATMEL -	{ 0, 0x1f, spi_flash_probe_atmel, }, -#endif -#ifdef CONFIG_SPI_FLASH_EON -	{ 0, 0x1c, spi_flash_probe_eon, }, -#endif -#ifdef CONFIG_SPI_FLASH_GIGADEVICE -	{ 0, 0xc8, spi_flash_probe_gigadevice, }, -#endif -#ifdef CONFIG_SPI_FLASH_MACRONIX -	{ 0, 0xc2, spi_flash_probe_macronix, }, -#endif -#ifdef CONFIG_SPI_FLASH_SPANSION -	{ 0, 0x01, spi_flash_probe_spansion, }, -#endif -#ifdef CONFIG_SPI_FLASH_SST -	{ 0, 0xbf, spi_flash_probe_sst, }, -#endif -#ifdef CONFIG_SPI_FLASH_STMICRO -	{ 0, 0x20, spi_flash_probe_stmicro, }, -#endif -#ifdef CONFIG_SPI_FLASH_WINBOND -	{ 0, 0xef, spi_flash_probe_winbond, }, -#endif -#ifdef CONFIG_SPI_FRAM_RAMTRON -	{ 6, 0xc2, spi_fram_probe_ramtron, }, -# undef IDCODE_CONT_LEN -# define IDCODE_CONT_LEN 6 -#endif -	/* Keep it sorted by best detection */ -#ifdef CONFIG_SPI_FLASH_STMICRO -	{ 0, 0xff, spi_flash_probe_stmicro, }, -#endif -#ifdef CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC -	{ 0, 0xff, spi_fram_probe_ramtron, }, -#endif -}; -#define IDCODE_LEN (IDCODE_CONT_LEN + IDCODE_PART_LEN) - -struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, -		unsigned int max_hz, unsigned int spi_mode) -{ -	struct spi_slave *spi; -	struct spi_flash *flash = NULL; -	int ret, i, shift; -	u8 idcode[IDCODE_LEN], *idp; - -	spi = spi_setup_slave(bus, cs, max_hz, spi_mode); -	if (!spi) { -		printf("SF: Failed to set up slave\n"); -		return NULL; -	} - -	ret = spi_claim_bus(spi); -	if (ret) { -		debug("SF: Failed to claim SPI bus: %d\n", ret); -		goto err_claim_bus; -	} - -	/* Read the ID codes */ -	ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode)); -	if (ret) -		goto err_read_id; - -#ifdef DEBUG -	printf("SF: Got idcodes\n"); -	print_buffer(0, idcode, 1, sizeof(idcode), 0); -#endif - -	/* count the number of continuation bytes */ -	for (shift = 0, idp = idcode; -	     shift < IDCODE_CONT_LEN && *idp == 0x7f; -	     ++shift, ++idp) -		continue; - -	/* search the table for matches in shift and id */ -	for (i = 0; i < ARRAY_SIZE(flashes); ++i) -		if (flashes[i].shift == shift && flashes[i].idcode == *idp) { -			/* we have a match, call probe */ -			flash = flashes[i].probe(spi, idp); -			if (flash) -				break; -		} - -	if (!flash) { -		printf("SF: Unsupported manufacturer %02x\n", *idp); -		goto err_manufacturer_probe; -	} - -#ifdef CONFIG_SPI_FLASH_BAR -	/* Configure the BAR - disover bank cmds and read current bank  */ -	ret = spi_flash_bank_config(flash, *idp); -	if (ret < 0) -		goto err_manufacturer_probe; -#endif - -#ifdef CONFIG_OF_CONTROL -	if (spi_flash_decode_fdt(gd->fdt_blob, flash)) { -		debug("SF: FDT decode error\n"); -		goto err_manufacturer_probe; -	} -#endif -#ifndef CONFIG_SPL_BUILD -	printf("SF: Detected %s with page size ", flash->name); -	print_size(flash->sector_size, ", total "); -	print_size(flash->size, ""); -	if (flash->memory_map) -		printf(", mapped at %p", flash->memory_map); -	puts("\n"); -#endif -#ifndef CONFIG_SPI_FLASH_BAR -	if (flash->size > SPI_FLASH_16MB_BOUN) { -		puts("SF: Warning - Only lower 16MiB accessible,"); -		puts(" Full access #define CONFIG_SPI_FLASH_BAR\n"); -	} -#endif - -	spi_release_bus(spi); - -	return flash; - -err_manufacturer_probe: -err_read_id: -	spi_release_bus(spi); -err_claim_bus: -	spi_free_slave(spi); -	return NULL; -} - -void *spi_flash_do_alloc(int offset, int size, struct spi_slave *spi, -			 const char *name) -{ -	struct spi_flash *flash; -	void *ptr; - -	ptr = malloc(size); -	if (!ptr) { -		debug("SF: Failed to allocate memory\n"); -		return NULL; -	} -	memset(ptr, '\0', size); -	flash = (struct spi_flash *)(ptr + offset); - -	/* Set up some basic fields - caller will sort out sizes */ -	flash->spi = spi; -	flash->name = name; -	flash->poll_cmd = CMD_READ_STATUS; - -	flash->read = spi_flash_cmd_read_fast; -	flash->write = spi_flash_cmd_write_multi; -	flash->erase = spi_flash_cmd_erase; - -	return flash; -} - -void spi_flash_free(struct spi_flash *flash) -{ -	spi_free_slave(flash->spi); -	free(flash); -} diff --git a/drivers/mtd/spi/sst.c b/drivers/mtd/spi/sst.c deleted file mode 100644 index 256867c84..000000000 --- a/drivers/mtd/spi/sst.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Driver for SST serial flashes - * - * (C) Copyright 2000-2002 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * Copyright 2008, Network Appliance Inc. - * Jason McMullan <mcmullan@netapp.com> - * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. - * TsiChung Liew (Tsi-Chung.Liew@freescale.com) - * Copyright (c) 2008-2009 Analog Devices Inc. - * - * Licensed under the GPL-2 or later. - */ - -#include <common.h> -#include <malloc.h> -#include <spi_flash.h> - -#include "spi_flash_internal.h" - -#define CMD_SST_BP		0x02	/* Byte Program */ -#define CMD_SST_AAI_WP		0xAD	/* Auto Address Incr Word Program */ - -#define SST_SR_WIP		(1 << 0)	/* Write-in-Progress */ -#define SST_SR_WEL		(1 << 1)	/* Write enable */ -#define SST_SR_BP0		(1 << 2)	/* Block Protection 0 */ -#define SST_SR_BP1		(1 << 3)	/* Block Protection 1 */ -#define SST_SR_BP2		(1 << 4)	/* Block Protection 2 */ -#define SST_SR_AAI		(1 << 6)	/* Addressing mode */ -#define SST_SR_BPL		(1 << 7)	/* BP bits lock */ - -#define SST_FEAT_WP		(1 << 0)	/* Supports AAI word program */ -#define SST_FEAT_MBP		(1 << 1)	/* Supports multibyte program */ - -struct sst_spi_flash_params { -	u8 idcode1; -	u8 flags; -	u16 nr_sectors; -	const char *name; -}; - -struct sst_spi_flash { -	struct spi_flash flash; -	const struct sst_spi_flash_params *params; -}; - -static const struct sst_spi_flash_params sst_spi_flash_table[] = { -	{ -		.idcode1 = 0x8d, -		.flags = SST_FEAT_WP, -		.nr_sectors = 128, -		.name = "SST25VF040B", -	}, -	{ -		.idcode1 = 0x8e, -		.flags = SST_FEAT_WP, -		.nr_sectors = 256, -		.name = "SST25VF080B", -	}, -	{ -		.idcode1 = 0x41, -		.flags = SST_FEAT_WP, -		.nr_sectors = 512, -		.name = "SST25VF016B", -	}, -	{ -		.idcode1 = 0x4a, -		.flags = SST_FEAT_WP, -		.nr_sectors = 1024, -		.name = "SST25VF032B", -	}, -	{ -		.idcode1 = 0x4b, -		.flags = SST_FEAT_MBP, -		.nr_sectors = 2048, -		.name = "SST25VF064C", -	}, -	{ -		.idcode1 = 0x01, -		.flags = SST_FEAT_WP, -		.nr_sectors = 16, -		.name = "SST25WF512", -	}, -	{ -		.idcode1 = 0x02, -		.flags = SST_FEAT_WP, -		.nr_sectors = 32, -		.name = "SST25WF010", -	}, -	{ -		.idcode1 = 0x03, -		.flags = SST_FEAT_WP, -		.nr_sectors = 64, -		.name = "SST25WF020", -	}, -	{ -		.idcode1 = 0x04, -		.flags = SST_FEAT_WP, -		.nr_sectors = 128, -		.name = "SST25WF040", -	}, -	{ -		.idcode1 = 0x05, -		.flags = SST_FEAT_WP, -		.nr_sectors = 256, -		.name = "SST25WF080", -	}, -}; - -static int -sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf) -{ -	int ret; -	u8 cmd[4] = { -		CMD_SST_BP, -		offset >> 16, -		offset >> 8, -		offset, -	}; - -	debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", -	      spi_w8r8(flash->spi, CMD_READ_STATUS), buf, cmd[0], offset); - -	ret = spi_flash_cmd_write_enable(flash); -	if (ret) -		return ret; - -	ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd), buf, 1); -	if (ret) -		return ret; - -	return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); -} - -static int -sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, const void *buf) -{ -	size_t actual, cmd_len; -	int ret; -	u8 cmd[4]; - -	ret = spi_claim_bus(flash->spi); -	if (ret) { -		debug("SF: Unable to claim SPI bus\n"); -		return ret; -	} - -	/* If the data is not word aligned, write out leading single byte */ -	actual = offset % 2; -	if (actual) { -		ret = sst_byte_write(flash, offset, buf); -		if (ret) -			goto done; -	} -	offset += actual; - -	ret = spi_flash_cmd_write_enable(flash); -	if (ret) -		goto done; - -	cmd_len = 4; -	cmd[0] = CMD_SST_AAI_WP; -	cmd[1] = offset >> 16; -	cmd[2] = offset >> 8; -	cmd[3] = offset; - -	for (; actual < len - 1; actual += 2) { -		debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", -		      spi_w8r8(flash->spi, CMD_READ_STATUS), buf + actual, -		      cmd[0], offset); - -		ret = spi_flash_cmd_write(flash->spi, cmd, cmd_len, -					buf + actual, 2); -		if (ret) { -			debug("SF: sst word program failed\n"); -			break; -		} - -		ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); -		if (ret) -			break; - -		cmd_len = 1; -		offset += 2; -	} - -	if (!ret) -		ret = spi_flash_cmd_write_disable(flash); - -	/* If there is a single trailing byte, write it out */ -	if (!ret && actual != len) -		ret = sst_byte_write(flash, offset, buf + actual); - - done: -	debug("SF: sst: program %s %zu bytes @ 0x%zx\n", -	      ret ? "failure" : "success", len, offset - actual); - -	spi_release_bus(flash->spi); -	return ret; -} - -struct spi_flash * -spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode) -{ -	const struct sst_spi_flash_params *params; -	struct sst_spi_flash *stm; -	size_t i; - -	for (i = 0; i < ARRAY_SIZE(sst_spi_flash_table); ++i) { -		params = &sst_spi_flash_table[i]; -		if (params->idcode1 == idcode[2]) -			break; -	} - -	if (i == ARRAY_SIZE(sst_spi_flash_table)) { -		debug("SF: Unsupported SST ID %02x\n", idcode[1]); -		return NULL; -	} - -	stm = spi_flash_alloc(struct sst_spi_flash, spi, params->name); -	if (!stm) { -		debug("SF: Failed to allocate memory\n"); -		return NULL; -	} - -	stm->params = params; - -	if (stm->params->flags & SST_FEAT_WP) -		stm->flash.write = sst_write_wp; -	stm->flash.page_size = 256; -	stm->flash.sector_size = 4096; -	stm->flash.size = stm->flash.sector_size * params->nr_sectors; - -	/* Flash powers up read-only, so clear BP# bits */ -	spi_flash_cmd_write_status(&stm->flash, 0); - -	return &stm->flash; -} diff --git a/drivers/mtd/spi/stmicro.c b/drivers/mtd/spi/stmicro.c deleted file mode 100644 index c5fa64e37..000000000 --- a/drivers/mtd/spi/stmicro.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * (C) Copyright 2000-2002 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * Copyright 2008, Network Appliance Inc. - * Jason McMullan <mcmullan@netapp.com> - * - * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. - * TsiChung Liew (Tsi-Chung.Liew@freescale.com) - * - * SPDX-License-Identifier:	GPL-2.0+ - */ - -#include <common.h> -#include <malloc.h> -#include <spi_flash.h> - -#include "spi_flash_internal.h" - -/* M25Pxx-specific commands */ -#define CMD_M25PXX_RES	0xab	/* Release from DP, and Read Signature */ - -struct stmicro_spi_flash_params { -	u16 id; -	u16 pages_per_sector; -	u16 nr_sectors; -	const char *name; -}; - -static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = { -	{ -		.id = 0x2011, -		.pages_per_sector = 128, -		.nr_sectors = 4, -		.name = "M25P10", -	}, -	{ -		.id = 0x2015, -		.pages_per_sector = 256, -		.nr_sectors = 32, -		.name = "M25P16", -	}, -	{ -		.id = 0x2012, -		.pages_per_sector = 256, -		.nr_sectors = 4, -		.name = "M25P20", -	}, -	{ -		.id = 0x2016, -		.pages_per_sector = 256, -		.nr_sectors = 64, -		.name = "M25P32", -	}, -	{ -		.id = 0x2013, -		.pages_per_sector = 256, -		.nr_sectors = 8, -		.name = "M25P40", -	}, -	{ -		.id = 0x2017, -		.pages_per_sector = 256, -		.nr_sectors = 128, -		.name = "M25P64", -	}, -	{ -		.id = 0x2014, -		.pages_per_sector = 256, -		.nr_sectors = 16, -		.name = "M25P80", -	}, -	{ -		.id = 0x2018, -		.pages_per_sector = 1024, -		.nr_sectors = 64, -		.name = "M25P128", -	}, -	{ -		.id = 0xba16, -		.pages_per_sector = 256, -		.nr_sectors = 64, -		.name = "N25Q32", -	}, -	{ -		.id = 0xbb16, -		.pages_per_sector = 256, -		.nr_sectors = 64, -		.name = "N25Q32A", -	}, -	{ -		.id = 0xba17, -		.pages_per_sector = 256, -		.nr_sectors = 128, -		.name = "N25Q064", -	}, -	{ -		.id = 0xbb17, -		.pages_per_sector = 256, -		.nr_sectors = 128, -		.name = "N25Q64A", -	}, -	{ -		.id = 0xba18, -		.pages_per_sector = 256, -		.nr_sectors = 256, -		.name = "N25Q128", -	}, -	{ -		.id = 0xbb18, -		.pages_per_sector = 256, -		.nr_sectors = 256, -		.name = "N25Q128A", -	}, -	{ -		.id = 0xba19, -		.pages_per_sector = 256, -		.nr_sectors = 512, -		.name = "N25Q256", -	}, -	{ -		.id = 0xbb19, -		.pages_per_sector = 256, -		.nr_sectors = 512, -		.name = "N25Q256A", -	}, -	{ -		.id = 0xba20, -		.pages_per_sector = 256, -		.nr_sectors = 1024, -		.name = "N25Q512", -	}, -	{ -		.id = 0xbb20, -		.pages_per_sector = 256, -		.nr_sectors = 1024, -		.name = "N25Q512A", -	}, -	{ -		.id = 0xba21, -		.pages_per_sector = 256, -		.nr_sectors = 2048, -		.name = "N25Q1024", -	}, -	{ -		.id = 0xbb21, -		.pages_per_sector = 256, -		.nr_sectors = 2048, -		.name = "N25Q1024A", -	}, -}; - -struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 *idcode) -{ -	const struct stmicro_spi_flash_params *params; -	struct spi_flash *flash; -	unsigned int i; -	u16 id; - -	if (idcode[0] == 0xff) { -		i = spi_flash_cmd(spi, CMD_M25PXX_RES, -				  idcode, 4); -		if (i) -			return NULL; -		if ((idcode[3] & 0xf0) == 0x10) { -			idcode[0] = 0x20; -			idcode[1] = 0x20; -			idcode[2] = idcode[3] + 1; -		} else { -			return NULL; -		} -	} - -	id = ((idcode[1] << 8) | idcode[2]); - -	for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) { -		params = &stmicro_spi_flash_table[i]; -		if (params->id == id) -			break; -	} - -	if (i == ARRAY_SIZE(stmicro_spi_flash_table)) { -		debug("SF: Unsupported STMicro ID %04x\n", id); -		return NULL; -	} - -	flash = spi_flash_alloc_base(spi, params->name); -	if (!flash) { -		debug("SF: Failed to allocate memory\n"); -		return NULL; -	} - -	flash->page_size = 256; -	flash->sector_size = 256 * params->pages_per_sector; -	flash->size = flash->sector_size * params->nr_sectors; - -	/* for >= 512MiB flashes, use flag status instead of read_status */ -	if (flash->size >= 0x4000000) -		flash->poll_cmd = CMD_FLAG_STATUS; - -	return flash; -} diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c deleted file mode 100644 index b31911a40..000000000 --- a/drivers/mtd/spi/winbond.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2008, Network Appliance Inc. - * Author: Jason McMullan <mcmullan <at> netapp.com> - * Licensed under the GPL-2 or later. - */ - -#include <common.h> -#include <malloc.h> -#include <spi_flash.h> - -#include "spi_flash_internal.h" - -struct winbond_spi_flash_params { -	uint16_t	id; -	uint16_t	nr_blocks; -	const char	*name; -}; - -static const struct winbond_spi_flash_params winbond_spi_flash_table[] = { -	{ -		.id			= 0x2014, -		.nr_blocks		= 16, -		.name			= "W25P80", -	}, -	{ -		.id			= 0x2015, -		.nr_blocks		= 32, -		.name			= "W25P16", -	}, -	{ -		.id			= 0x2016, -		.nr_blocks		= 64, -		.name			= "W25P32", -	}, -	{ -		.id			= 0x3013, -		.nr_blocks		= 8, -		.name			= "W25X40", -	}, -	{ -		.id			= 0x3015, -		.nr_blocks		= 32, -		.name			= "W25X16", -	}, -	{ -		.id			= 0x3016, -		.nr_blocks		= 64, -		.name			= "W25X32", -	}, -	{ -		.id			= 0x3017, -		.nr_blocks		= 128, -		.name			= "W25X64", -	}, -	{ -		.id			= 0x4014, -		.nr_blocks		= 16, -		.name			= "W25Q80BL/W25Q80BV", -	}, -	{ -		.id			= 0x4015, -		.nr_blocks		= 32, -		.name			= "W25Q16CL/W25Q16DV", -	}, -	{ -		.id			= 0x4016, -		.nr_blocks		= 64, -		.name			= "W25Q32BV/W25Q32FV_SPI", -	}, -	{ -		.id			= 0x4017, -		.nr_blocks		= 128, -		.name			= "W25Q64CV/W25Q64FV_SPI", -	}, -	{ -		.id			= 0x4018, -		.nr_blocks		= 256, -		.name			= "W25Q128BV/W25Q128FV_SPI", -	}, -	{ -		.id			= 0x4019, -		.nr_blocks		= 512, -		.name			= "W25Q256", -	}, -	{ -		.id			= 0x5014, -		.nr_blocks		= 16, -		.name			= "W25Q80BW", -	}, -	{ -		.id			= 0x6015, -		.nr_blocks		= 32, -		.name			= "W25Q16DW", -	}, -	{ -		.id			= 0x6016, -		.nr_blocks		= 64, -		.name			= "W25Q32DW/W25Q32FV_QPI", -	}, -	{ -		.id			= 0x6017, -		.nr_blocks		= 128, -		.name			= "W25Q64DW/W25Q64FV_QPI", -	}, -	{ -		.id			= 0x6018, -		.nr_blocks		= 256, -		.name			= "W25Q128FW/W25Q128FV_QPI", -	}, -}; - -struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode) -{ -	const struct winbond_spi_flash_params *params; -	struct spi_flash *flash; -	unsigned int i; - -	for (i = 0; i < ARRAY_SIZE(winbond_spi_flash_table); i++) { -		params = &winbond_spi_flash_table[i]; -		if (params->id == ((idcode[1] << 8) | idcode[2])) -			break; -	} - -	if (i == ARRAY_SIZE(winbond_spi_flash_table)) { -		debug("SF: Unsupported Winbond ID %02x%02x\n", -		      idcode[1], idcode[2]); -		return NULL; -	} - -	flash = spi_flash_alloc_base(spi, params->name); -	if (!flash) { -		debug("SF: Failed to allocate memory\n"); -		return NULL; -	} - -	flash->page_size = 256; -	flash->sector_size = (idcode[1] == 0x20) ? 65536 : 4096; -	flash->size = 4096 * 16 * params->nr_blocks; - -	return flash; -} diff --git a/drivers/net/4xx_enet.c b/drivers/net/4xx_enet.c index c98867dc4..381ec4286 100644 --- a/drivers/net/4xx_enet.c +++ b/drivers/net/4xx_enet.c @@ -1,5 +1,5 @@  /* - * SPDX-License-Identifier:	GPL-2.0	ibm-pibs + * SPDX-License-Identifier:	GPL-2.0	IBM-pibs   */  /*-----------------------------------------------------------------------------+   * diff --git a/drivers/net/npe/miiphy.c b/drivers/net/npe/miiphy.c index 07fcb60e9..002fb8113 100644 --- a/drivers/net/npe/miiphy.c +++ b/drivers/net/npe/miiphy.c @@ -1,5 +1,5 @@  /* - * SPDX-License-Identifier:	GPL-2.0	ibm-pibs + * SPDX-License-Identifier:	GPL-2.0	IBM-pibs   */  /*-----------------------------------------------------------------------------+    | diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 5936f9bd7..60ed92d20 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -7,7 +7,7 @@   *   Copyright 2010-2011 Freescale Semiconductor, Inc.   *   author Andy Fleming   * - * Some code get from linux kenrel + * Some code copied from linux kernel   * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org>   */  #include <miiphy.h> diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c index c4ed8ba6c..86ba6b523 100644 --- a/drivers/pci/pci_auto.c +++ b/drivers/pci/pci_auto.c @@ -297,7 +297,7 @@ void pciauto_config_init(struct pci_controller *hose)  {  	int i; -	hose->pci_io = hose->pci_mem = NULL; +	hose->pci_io = hose->pci_mem = hose->pci_prefetch = NULL;  	for (i = 0; i < hose->region_count; i++) {  		switch(hose->regions[i].flags) { diff --git a/drivers/power/power_core.c b/drivers/power/power_core.c index d79971ba8..29ccc831a 100644 --- a/drivers/power/power_core.c +++ b/drivers/power/power_core.c @@ -184,18 +184,21 @@ int do_pmic(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])  		if (argc < 4)  			return CMD_RET_USAGE; +		if (!p->pbat) { +			printf("%s is not a battery\n", p->name); +			return CMD_RET_FAILURE; +		} +  		if (strcmp(argv[3], "state") == 0)  			p->fg->fg_battery_check(p->pbat->fg, p);  		if (strcmp(argv[3], "charge") == 0) { -			if (p->pbat) { -				printf("BAT: %s charging (ctrl+c to break)\n", -				       p->name); -				if (p->low_power_mode) -					p->low_power_mode(); -				if (p->pbat->battery_charge) -					p->pbat->battery_charge(p); -			} +			printf("BAT: %s charging (ctrl+c to break)\n", +			       p->name); +			if (p->low_power_mode) +				p->low_power_mode(); +			if (p->pbat->battery_charge) +				p->pbat->battery_charge(p);  		}  		return CMD_RET_SUCCESS; diff --git a/drivers/sound/max98095.c b/drivers/sound/max98095.c index d69db58dc..febf4195b 100644 --- a/drivers/sound/max98095.c +++ b/drivers/sound/max98095.c @@ -52,7 +52,7 @@ int rate_table[] = {0, 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000,  static int max98095_i2c_write(unsigned int reg, unsigned char data)  {  	debug("%s: Write Addr : 0x%02X, Data :  0x%02X\n", -		__func__, reg, data); +	      __func__, reg, data);  	return i2c_write(g_max98095_i2c_dev_addr, reg, 1, &data, 1);  } @@ -71,7 +71,7 @@ static unsigned int max98095_i2c_read(unsigned int reg, unsigned char *data)  	ret = i2c_read(g_max98095_i2c_dev_addr, reg, 1, data, 1);  	if (ret != 0) {  		debug("%s: Error while reading register %#04x\n", -			__func__, reg); +		      __func__, reg);  		return -1;  	} @@ -138,43 +138,57 @@ static int rate_value(int rate, u8 *value)   * @return -1 for error  and 0  Success.   */  static int max98095_hw_params(struct max98095_priv *max98095, -		unsigned int rate, unsigned int bits_per_sample) +			      enum en_max_audio_interface aif_id, +			      unsigned int rate, unsigned int bits_per_sample)  {  	u8 regval;  	int error; +	unsigned short M98095_DAI_CLKMODE; +	unsigned short M98095_DAI_FORMAT; +	unsigned short M98095_DAI_FILTERS; + +	if (aif_id == AIF1) { +		M98095_DAI_CLKMODE = M98095_027_DAI1_CLKMODE; +		M98095_DAI_FORMAT = M98095_02A_DAI1_FORMAT; +		M98095_DAI_FILTERS = M98095_02E_DAI1_FILTERS; +	} else { +		M98095_DAI_CLKMODE = M98095_031_DAI2_CLKMODE; +		M98095_DAI_FORMAT = M98095_034_DAI2_FORMAT; +		M98095_DAI_FILTERS = M98095_038_DAI2_FILTERS; +	}  	switch (bits_per_sample) {  	case 16: -		error = max98095_update_bits(M98095_034_DAI2_FORMAT, -			M98095_DAI_WS, 0); +		error = max98095_update_bits(M98095_DAI_FORMAT, +					     M98095_DAI_WS, 0);  		break;  	case 24: -		error = max98095_update_bits(M98095_034_DAI2_FORMAT, -			M98095_DAI_WS, M98095_DAI_WS); +		error = max98095_update_bits(M98095_DAI_FORMAT, +					     M98095_DAI_WS, M98095_DAI_WS);  		break;  	default:  		debug("%s: Illegal bits per sample %d.\n", -			__func__, bits_per_sample); +		      __func__, bits_per_sample);  		return -1;  	}  	if (rate_value(rate, ®val)) {  		debug("%s: Failed to set sample rate to %d.\n", -			__func__, rate); +		      __func__, rate);  		return -1;  	}  	max98095->rate = rate; -	error |= max98095_update_bits(M98095_031_DAI2_CLKMODE, -		M98095_CLKMODE_MASK, regval); +	error |= max98095_update_bits(M98095_DAI_CLKMODE, +				      M98095_CLKMODE_MASK, regval);  	/* Update sample rate mode */  	if (rate < 50000) -		error |= max98095_update_bits(M98095_038_DAI2_FILTERS, -			M98095_DAI_DHF, 0); +		error |= max98095_update_bits(M98095_DAI_FILTERS, +					      M98095_DAI_DHF, 0);  	else -		error |= max98095_update_bits(M98095_038_DAI2_FILTERS, -			M98095_DAI_DHF, M98095_DAI_DHF); +		error |= max98095_update_bits(M98095_DAI_FILTERS, +					      M98095_DAI_DHF, M98095_DAI_DHF);  	if (error < 0) {  		debug("%s: Error setting hardware params.\n", __func__); @@ -193,7 +207,7 @@ static int max98095_hw_params(struct max98095_priv *max98095,   * @return -1 for error and 0 success.   */  static int max98095_set_sysclk(struct max98095_priv *max98095, -				unsigned int freq) +			       unsigned int freq)  {  	int error = 0; @@ -235,22 +249,39 @@ static int max98095_set_sysclk(struct max98095_priv *max98095,   *   * @return -1 for error and 0  Success.   */ -static int max98095_set_fmt(struct max98095_priv *max98095, int fmt) +static int max98095_set_fmt(struct max98095_priv *max98095, int fmt, +			    enum en_max_audio_interface aif_id)  {  	u8 regval = 0;  	int error = 0; +	unsigned short M98095_DAI_CLKCFG_HI; +	unsigned short M98095_DAI_CLKCFG_LO; +	unsigned short M98095_DAI_FORMAT; +	unsigned short M98095_DAI_CLOCK;  	if (fmt == max98095->fmt)  		return 0;  	max98095->fmt = fmt; +	if (aif_id == AIF1) { +		M98095_DAI_CLKCFG_HI = M98095_028_DAI1_CLKCFG_HI; +		M98095_DAI_CLKCFG_LO = M98095_029_DAI1_CLKCFG_LO; +		M98095_DAI_FORMAT = M98095_02A_DAI1_FORMAT; +		M98095_DAI_CLOCK = M98095_02B_DAI1_CLOCK; +	} else { +		M98095_DAI_CLKCFG_HI = M98095_032_DAI2_CLKCFG_HI; +		M98095_DAI_CLKCFG_LO = M98095_033_DAI2_CLKCFG_LO; +		M98095_DAI_FORMAT = M98095_034_DAI2_FORMAT; +		M98095_DAI_CLOCK = M98095_035_DAI2_CLOCK; +	} +  	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {  	case SND_SOC_DAIFMT_CBS_CFS:  		/* Slave mode PLL */ -		error |= max98095_i2c_write(M98095_032_DAI2_CLKCFG_HI, +		error |= max98095_i2c_write(M98095_DAI_CLKCFG_HI,  					0x80); -		error |= max98095_i2c_write(M98095_033_DAI2_CLKCFG_LO, +		error |= max98095_i2c_write(M98095_DAI_CLKCFG_LO,  					0x00);  		break;  	case SND_SOC_DAIFMT_CBM_CFM: @@ -292,12 +323,13 @@ static int max98095_set_fmt(struct max98095_priv *max98095, int fmt)  		return -1;  	} -	error |= max98095_update_bits(M98095_034_DAI2_FORMAT, -		M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI | -		M98095_DAI_WCI, regval); +	error |= max98095_update_bits(M98095_DAI_FORMAT, +				      M98095_DAI_MAS | M98095_DAI_DLY | +				      M98095_DAI_BCI | M98095_DAI_WCI, +				      regval); -	error |= max98095_i2c_write(M98095_035_DAI2_CLOCK, -		M98095_DAI_BSEL64); +	error |= max98095_i2c_write(M98095_DAI_CLOCK, +				    M98095_DAI_BSEL64);  	if (error < 0) {  		debug("%s: Error setting i2s format.\n", __func__); @@ -354,7 +386,8 @@ static int max98095_reset(void)   *   * @returns -1 for error  and 0 Success.   */ -static int max98095_device_init(struct max98095_priv *max98095) +static int max98095_device_init(struct max98095_priv *max98095, +				enum en_max_audio_interface aif_id)  {  	unsigned char id;  	int error = 0; @@ -374,7 +407,7 @@ static int max98095_device_init(struct max98095_priv *max98095)  	error = max98095_i2c_read(M98095_0FF_REV_ID, &id);  	if (error < 0) {  		debug("%s: Failure reading hardware revision: %d\n", -			__func__, id); +		      __func__, id);  		goto err_access;  	}  	debug("%s: Hardware revision: %c\n", __func__, (id - 0x40) + 'A'); @@ -385,26 +418,28 @@ static int max98095_device_init(struct max98095_priv *max98095)  	 * initialize registers to hardware default configuring audio  	 * interface2 to DAC  	 */ -	error |= max98095_i2c_write(M98095_048_MIX_DAC_LR, -		M98095_DAI2M_TO_DACL|M98095_DAI2M_TO_DACR); +	if (aif_id == AIF1) +		error |= max98095_i2c_write(M98095_048_MIX_DAC_LR, +					    M98095_DAI1L_TO_DACL | +					    M98095_DAI1R_TO_DACR); +	else +		error |= max98095_i2c_write(M98095_048_MIX_DAC_LR, +					    M98095_DAI2M_TO_DACL | +					    M98095_DAI2M_TO_DACR);  	error |= max98095_i2c_write(M98095_092_PWR_EN_OUT, -			M98095_SPK_SPREADSPECTRUM); -	error |= max98095_i2c_write(M98095_045_CFG_DSP, M98095_DSPNORMAL); +				    M98095_SPK_SPREADSPECTRUM);  	error |= max98095_i2c_write(M98095_04E_CFG_HP, M98095_HPNORMAL); - -	error |= max98095_i2c_write(M98095_02C_DAI1_IOCFG, -			M98095_S1NORMAL|M98095_SDATA); - -	error |= max98095_i2c_write(M98095_036_DAI2_IOCFG, -			M98095_S2NORMAL|M98095_SDATA); - -	error |= max98095_i2c_write(M98095_040_DAI3_IOCFG, -			M98095_S3NORMAL|M98095_SDATA); +	if (aif_id == AIF1) +		error |= max98095_i2c_write(M98095_02C_DAI1_IOCFG, +					    M98095_S1NORMAL | M98095_SDATA); +	else +		error |= max98095_i2c_write(M98095_036_DAI2_IOCFG, +					    M98095_S2NORMAL | M98095_SDATA);  	/* take the codec out of the shut down */  	error |= max98095_update_bits(M98095_097_PWR_SYS, M98095_SHDNRUN, -			M98095_SHDNRUN); +				      M98095_SHDNRUN);  	/* route DACL and DACR output to HO and Spekers */  	error |= max98095_i2c_write(M98095_050_MIX_SPK_LEFT, 0x01); /* DACL */  	error |= max98095_i2c_write(M98095_051_MIX_SPK_RIGHT, 0x01);/* DACR */ @@ -422,7 +457,10 @@ static int max98095_device_init(struct max98095_priv *max98095)  	/* Enable DAIs */  	error |= max98095_i2c_write(M98095_093_BIAS_CTRL, 0x30); -	error |= max98095_i2c_write(M98095_096_PWR_DAC_CK, 0x07); +	if (aif_id == AIF1) +		error |= max98095_i2c_write(M98095_096_PWR_DAC_CK, 0x01); +	else +		error |= max98095_i2c_write(M98095_096_PWR_DAC_CK, 0x07);  err_access:  	if (error < 0) @@ -432,8 +470,9 @@ err_access:  }  static int max98095_do_init(struct sound_codec_info *pcodec_info, -			int sampling_rate, int mclk_freq, -			int bits_per_sample) +			    enum en_max_audio_interface aif_id, +			    int sampling_rate, int mclk_freq, +			    int bits_per_sample)  {  	int ret = 0; @@ -443,15 +482,15 @@ static int max98095_do_init(struct sound_codec_info *pcodec_info,  	/* shift the device address by 1 for 7 bit addressing */  	g_max98095_i2c_dev_addr = pcodec_info->i2c_dev_addr >> 1; -	if (pcodec_info->codec_type == CODEC_MAX_98095) +	if (pcodec_info->codec_type == CODEC_MAX_98095) {  		g_max98095_info.devtype = MAX98095; -	else { +	} else {  		debug("%s: Codec id [%d] not defined\n", __func__, -				pcodec_info->codec_type); +		      pcodec_info->codec_type);  		return -1;  	} -	ret = max98095_device_init(&g_max98095_info); +	ret = max98095_device_init(&g_max98095_info, aif_id);  	if (ret < 0) {  		debug("%s: max98095 codec chip init failed\n", __func__);  		return ret; @@ -463,14 +502,15 @@ static int max98095_do_init(struct sound_codec_info *pcodec_info,  		return ret;  	} -	ret = max98095_hw_params(&g_max98095_info, sampling_rate, -				bits_per_sample); +	ret = max98095_hw_params(&g_max98095_info, aif_id, sampling_rate, +				 bits_per_sample);  	if (ret == 0) {  		ret = max98095_set_fmt(&g_max98095_info, -					SND_SOC_DAIFMT_I2S | -					SND_SOC_DAIFMT_NB_NF | -					SND_SOC_DAIFMT_CBS_CFS); +				       SND_SOC_DAIFMT_I2S | +				       SND_SOC_DAIFMT_NB_NF | +				       SND_SOC_DAIFMT_CBS_CFS, +				       aif_id);  	}  	return ret; @@ -529,8 +569,9 @@ static int get_max98095_codec_values(struct sound_codec_info *pcodec_info,  }  /* max98095 Device Initialisation */ -int max98095_init(const void *blob, int sampling_rate, int mclk_freq, -			int bits_per_sample) +int max98095_init(const void *blob, enum en_max_audio_interface aif_id, +		  int sampling_rate, int mclk_freq, +		  int bits_per_sample)  {  	int ret;  	int old_bus = i2c_get_bus_num(); @@ -538,12 +579,12 @@ int max98095_init(const void *blob, int sampling_rate, int mclk_freq,  	if (get_max98095_codec_values(pcodec_info, blob) < 0) {  		debug("FDT Codec values failed\n"); -		 return -1; +		return -1;  	}  	i2c_set_bus_num(pcodec_info->i2c_bus); -	ret = max98095_do_init(pcodec_info, sampling_rate, mclk_freq, -				bits_per_sample); +	ret = max98095_do_init(pcodec_info, aif_id, sampling_rate, mclk_freq, +			       bits_per_sample);  	i2c_set_bus_num(old_bus);  	return ret; diff --git a/drivers/sound/max98095.h b/drivers/sound/max98095.h index ae5eb14db..44b1e3a97 100644 --- a/drivers/sound/max98095.h +++ b/drivers/sound/max98095.h @@ -11,6 +11,12 @@  #ifndef _MAX98095_H  #define _MAX98095_H +/*  Available audio interface ports in wm8994 codec */ +enum en_max_audio_interface { +	AIF1 = 1, +	AIF2, +}; +  /*   * MAX98095 Registers Definition   */ @@ -305,7 +311,7 @@   *   * @returns -1 for error and 0 Success.   */ -int max98095_init(const void *blob, int sampling_rate, int mclk_freq, -			int bits_per_sample); +int max98095_init(const void *blob, enum en_max_audio_interface aif_id, +		  int sampling_rate, int mclk_freq, int bits_per_sample);  #endif diff --git a/drivers/sound/samsung-i2s.c b/drivers/sound/samsung-i2s.c index 49921e552..47f155f85 100644 --- a/drivers/sound/samsung-i2s.c +++ b/drivers/sound/samsung-i2s.c @@ -65,9 +65,7 @@ static void i2s_txctrl(struct i2s_reg *i2s_reg, int on)  	if (on) {  		con |= CON_ACTIVE;  		con &= ~CON_TXCH_PAUSE; -  	} else { -  		con |=  CON_TXCH_PAUSE;  		con &= ~CON_ACTIVE;  	} @@ -172,7 +170,7 @@ int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt)  		break;  	default:  		debug("%s: Invalid format priority [0x%x]\n", __func__, -			(fmt & SND_SOC_DAIFMT_FORMAT_MASK)); +		      (fmt & SND_SOC_DAIFMT_FORMAT_MASK));  		return -1;  	} @@ -191,7 +189,7 @@ int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt)  		break;  	default:  		debug("%s: Invalid clock ploarity input [0x%x]\n", __func__, -			(fmt & SND_SOC_DAIFMT_INV_MASK)); +		      (fmt & SND_SOC_DAIFMT_INV_MASK));  		return -1;  	} @@ -209,7 +207,7 @@ int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt)  		break;  	default:  		debug("%s: Invalid master selection [0x%x]\n", __func__, -			(fmt & SND_SOC_DAIFMT_MASTER_MASK)); +		      (fmt & SND_SOC_DAIFMT_MASTER_MASK));  		return -1;  	} @@ -250,7 +248,7 @@ int i2s_set_samplesize(struct i2s_reg *i2s_reg, unsigned int blc)  		break;  	default:  		debug("%s: Invalid sample size input [0x%x]\n", -			__func__, blc); +		      __func__, blc);  		return -1;  	}  	writel(mod, &i2s_reg->mod); @@ -301,27 +299,58 @@ int i2s_tx_init(struct i2stx_info *pi2s_tx)  	int ret;  	struct i2s_reg *i2s_reg =  				(struct i2s_reg *)pi2s_tx->base_address; +	if (pi2s_tx->id == 0) { +		/* Initialize GPIO for I2S-0 */ +		exynos_pinmux_config(PERIPH_ID_I2S0, 0); -	/* Initialize GPIO for I2s */ -	exynos_pinmux_config(PERIPH_ID_I2S1, 0); +		/* Set EPLL Clock */ +		ret = set_epll_clk(pi2s_tx->samplingrate * pi2s_tx->rfs * 4); +	} else if (pi2s_tx->id == 1) { +		/* Initialize GPIO for I2S-1 */ +		exynos_pinmux_config(PERIPH_ID_I2S1, 0); + +		/* Set EPLL Clock */ +		ret = set_epll_clk(pi2s_tx->audio_pll_clk); +	} else { +		debug("%s: unsupported i2s-%d bus\n", __func__, pi2s_tx->id); +		return -1; +	} -	/* Set EPLL Clock */ -	ret = set_epll_clk(pi2s_tx->audio_pll_clk);  	if (ret != 0) { -		debug("%s: epll clock set rate falied\n", __func__); +		debug("%s: epll clock set rate failed\n", __func__);  		return -1;  	} -	/* Select Clk Source for Audio1 */ -	set_i2s_clk_source(); +	/* Select Clk Source for Audio 0 or 1 */ +	ret = set_i2s_clk_source(pi2s_tx->id); +	if (ret == -1) { +		debug("%s: unsupported clock for i2s-%d\n", __func__, +		      pi2s_tx->id); +		return -1; +	} + +	if (pi2s_tx->id == 0) { +		/*Reset the i2s module */ +		writel(CON_RESET, &i2s_reg->con); -	/* Set Prescaler to get MCLK */ -	set_i2s_clk_prescaler(pi2s_tx->audio_pll_clk, -				(pi2s_tx->samplingrate * (pi2s_tx->rfs))); +		writel(MOD_OP_CLK | MOD_RCLKSRC, &i2s_reg->mod); +		/* set i2s prescaler */ +		writel(PSREN | PSVAL, &i2s_reg->psr); +	} else { +		/* Set Prescaler to get MCLK */ +		ret = set_i2s_clk_prescaler(pi2s_tx->audio_pll_clk, +				(pi2s_tx->samplingrate * (pi2s_tx->rfs)), +				pi2s_tx->id); +	} +	if (ret == -1) { +		debug("%s: unsupported prescalar for i2s-%d\n", __func__, +		      pi2s_tx->id); +		return -1; +	}  	/* Configure I2s format */  	ret = i2s_set_fmt(i2s_reg, (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | -				SND_SOC_DAIFMT_CBM_CFM)); +			  SND_SOC_DAIFMT_CBM_CFM));  	if (ret == 0) {  		i2s_set_lr_framesize(i2s_reg, pi2s_tx->rfs);  		ret = i2s_set_samplesize(i2s_reg, pi2s_tx->bitspersample); diff --git a/drivers/sound/sound.c b/drivers/sound/sound.c index 6fcc75da5..9b8ce5a9e 100644 --- a/drivers/sound/sound.c +++ b/drivers/sound/sound.c @@ -36,8 +36,7 @@ static int get_sound_i2s_values(struct i2stx_info *i2s, const void *blob)  	int error = 0;  	int base; -	node = fdtdec_next_compatible(blob, 0, -					COMPAT_SAMSUNG_EXYNOS5_SOUND); +	node = fdt_path_offset(blob, "i2s");  	if (node <= 0) {  		debug("EXYNOS_SOUND: No node for sound in device tree\n");  		return -1; @@ -80,6 +79,11 @@ static int get_sound_i2s_values(struct i2stx_info *i2s, const void *blob)  				node, "samsung,i2s-bit-clk-framesize", -1);  	error |= i2s->bfs;  	debug("bfs = %d\n", i2s->bfs); + +	i2s->id = fdtdec_get_int(blob, node, "samsung,i2s-id", -1); +	error |= i2s->id; +	debug("id = %d\n", i2s->id); +  	if (error == -1) {  		debug("fail to get sound i2s node properties\n");  		return -1; @@ -92,6 +96,7 @@ static int get_sound_i2s_values(struct i2stx_info *i2s, const void *blob)  	i2s->channels = I2S_CHANNELS;  	i2s->rfs = I2S_RFS;  	i2s->bfs = I2S_BFS; +	i2s->id = 0;  #endif  	return 0;  } @@ -111,7 +116,7 @@ static int codec_init(const void *blob, struct i2stx_info *pi2s_tx)  	int node;  	/* Get the node from FDT for sound */ -	node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS5_SOUND); +	node = fdt_path_offset(blob, "i2s");  	if (node <= 0) {  		debug("EXYNOS_SOUND: No node for sound in device tree\n");  		debug("node = %d\n", node); @@ -130,14 +135,15 @@ static int codec_init(const void *blob, struct i2stx_info *pi2s_tx)  #endif  	if (!strcmp(codectype, "wm8994")) {  		/* Check the codec type and initialise the same */ -		ret = wm8994_init(blob, WM8994_AIF2, -			pi2s_tx->samplingrate, -			(pi2s_tx->samplingrate * (pi2s_tx->rfs)), -			pi2s_tx->bitspersample, pi2s_tx->channels); +		ret = wm8994_init(blob, pi2s_tx->id + 1, +				  pi2s_tx->samplingrate, +				  (pi2s_tx->samplingrate * (pi2s_tx->rfs)), +				  pi2s_tx->bitspersample, pi2s_tx->channels);  	} else if (!strcmp(codectype, "max98095")) { -		ret = max98095_init(blob, pi2s_tx->samplingrate, -				(pi2s_tx->samplingrate * (pi2s_tx->rfs)), -				pi2s_tx->bitspersample); +		ret = max98095_init(blob, pi2s_tx->id + 1, +				    pi2s_tx->samplingrate, +				    (pi2s_tx->samplingrate * (pi2s_tx->rfs)), +				    pi2s_tx->bitspersample);  	} else {  		debug("%s: Unknown codec type %s\n", __func__, codectype);  		return -1; @@ -230,7 +236,7 @@ int sound_play(uint32_t msec, uint32_t frequency)  	}  	sound_prepare_buffer((unsigned short *)data, -				data_size / sizeof(unsigned short), frequency); +			     data_size / sizeof(unsigned short), frequency);  	while (msec >= 1000) {  		ret = i2s_transfer_tx_data(&g_i2stx_pri, data, diff --git a/drivers/sound/wm8994.c b/drivers/sound/wm8994.c index 37e354c5c..f8e9a6ead 100644 --- a/drivers/sound/wm8994.c +++ b/drivers/sound/wm8994.c @@ -432,12 +432,12 @@ static int configure_aif_clock(struct wm8994_priv *wm8994, int aif)  	int ret;  	/* AIF(1/0) register adress offset calculated */ -	if (aif) +	if (aif-1)  		offset = 4;  	else  		offset = 0; -	switch (wm8994->sysclk[aif]) { +	switch (wm8994->sysclk[aif-1]) {  	case WM8994_SYSCLK_MCLK1:  		reg1 |= SEL_MCLK1;  		rate = wm8994->mclk[0]; @@ -460,7 +460,7 @@ static int configure_aif_clock(struct wm8994_priv *wm8994, int aif)  	default:  		debug("%s: Invalid input clock selection [%d]\n", -		      __func__, wm8994->sysclk[aif]); +		      __func__, wm8994->sysclk[aif-1]);  		return -1;  	} @@ -470,13 +470,18 @@ static int configure_aif_clock(struct wm8994_priv *wm8994, int aif)  		reg1 |= WM8994_AIF1CLK_DIV;  	} -	wm8994->aifclk[aif] = rate; +	wm8994->aifclk[aif-1] = rate;  	ret = wm8994_update_bits(WM8994_AIF1_CLOCKING_1 + offset,  				WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV,  				reg1); -	ret |= wm8994_update_bits(WM8994_CLOCKING_1, +	if (aif == WM8994_AIF1) +		ret |= wm8994_update_bits(WM8994_CLOCKING_1, +			WM8994_AIF1DSPCLK_ENA_MASK | WM8994_SYSDSPCLK_ENA_MASK, +			WM8994_AIF1DSPCLK_ENA | WM8994_SYSDSPCLK_ENA); +	else if (aif == WM8994_AIF2) +		ret |= wm8994_update_bits(WM8994_CLOCKING_1,  			WM8994_SYSCLK_SRC | WM8994_AIF2DSPCLK_ENA_MASK |  			WM8994_SYSDSPCLK_ENA_MASK, WM8994_SYSCLK_SRC |  			WM8994_AIF2DSPCLK_ENA | WM8994_SYSDSPCLK_ENA); @@ -536,7 +541,7 @@ static int wm8994_set_sysclk(struct wm8994_priv *wm8994, int aif_id,  					break;  			if (i == ARRAY_SIZE(opclk_divs)) {  				debug("%s frequency divisor not found\n", -					__func__); +				      __func__);  				return -1;  			}  			ret = wm8994_update_bits(WM8994_CLOCKING_2, @@ -554,7 +559,7 @@ static int wm8994_set_sysclk(struct wm8994_priv *wm8994, int aif_id,  		return -1;  	} -	ret |= configure_aif_clock(wm8994, aif_id - 1); +	ret |= configure_aif_clock(wm8994, aif_id);  	if (ret < 0) {  		debug("%s: codec register access error\n", __func__); @@ -608,13 +613,46 @@ static int wm8994_init_volume_aif2_dac1(void)  }  /* + * Initializes Volume for AIF1 to HP path + * + * @returns -1 for error  and 0 Success. + * + */ +static int wm8994_init_volume_aif1_dac1(void) +{ +	int ret = 0; + +	/* Unmute AIF1DAC */ +	ret |= wm8994_i2c_write(WM8994_AIF1_DAC_FILTERS_1, 0x0000); + +	ret |= wm8994_update_bits(WM8994_DAC1_LEFT_VOLUME, +			WM8994_DAC1_VU_MASK | WM8994_DAC1L_VOL_MASK | +			WM8994_DAC1L_MUTE_MASK, WM8994_DAC1_VU | 0xc0); + +	ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_VOLUME, +			WM8994_DAC1_VU_MASK | WM8994_DAC1R_VOL_MASK | +			WM8994_DAC1R_MUTE_MASK, WM8994_DAC1_VU | 0xc0); +	/* Head Phone Volume */ +	ret |= wm8994_i2c_write(WM8994_LEFT_OUTPUT_VOLUME, 0x12D); +	ret |= wm8994_i2c_write(WM8994_RIGHT_OUTPUT_VOLUME, 0x12D); + +	if (ret < 0) { +		debug("%s: codec register access error\n", __func__); +		return -1; +	} + +	return 0; +} + +/*   * Intialise wm8994 codec device   *   * @param wm8994	wm8994 information   *   * @returns -1 for error  and 0 Success.   */ -static int wm8994_device_init(struct wm8994_priv *wm8994) +static int wm8994_device_init(struct wm8994_priv *wm8994, +			      enum en_audio_interface aif_id)  {  	const char *devname;  	unsigned short reg_data; @@ -661,13 +699,30 @@ static int wm8994_device_init(struct wm8994_priv *wm8994)  	ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1,  				WM8994_HPOUT1R_ENA_MASK, WM8994_HPOUT1R_ENA); -	/* Power enable for AIF2 and DAC1 */ -	ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_5, -		WM8994_AIF2DACL_ENA_MASK | WM8994_AIF2DACR_ENA_MASK | -		WM8994_DAC1L_ENA_MASK | WM8994_DAC1R_ENA_MASK, -		WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA | WM8994_DAC1L_ENA | -		WM8994_DAC1R_ENA); +	if (aif_id == WM8994_AIF1) { +		ret |= wm8994_i2c_write(WM8994_POWER_MANAGEMENT_2, +					WM8994_TSHUT_ENA | WM8994_MIXINL_ENA | +					WM8994_MIXINR_ENA | WM8994_IN2L_ENA | +					WM8994_IN2R_ENA); + +		ret |= wm8994_i2c_write(WM8994_POWER_MANAGEMENT_4, +					WM8994_ADCL_ENA | WM8994_ADCR_ENA | +					WM8994_AIF1ADC1R_ENA | +					WM8994_AIF1ADC1L_ENA); +		/* Power enable for AIF1 and DAC1 */ +		ret |= wm8994_i2c_write(WM8994_POWER_MANAGEMENT_5, +					WM8994_AIF1DACL_ENA | +					WM8994_AIF1DACR_ENA | +					WM8994_DAC1L_ENA | WM8994_DAC1R_ENA); +	} else if (aif_id == WM8994_AIF2) { +		/* Power enable for AIF2 and DAC1 */ +		ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_5, +			WM8994_AIF2DACL_ENA_MASK | WM8994_AIF2DACR_ENA_MASK | +			WM8994_DAC1L_ENA_MASK | WM8994_DAC1R_ENA_MASK, +			WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA | +			WM8994_DAC1L_ENA | WM8994_DAC1R_ENA); +	}  	/* Head Phone Initialisation */  	ret |= wm8994_update_bits(WM8994_ANALOGUE_HP_1,  		WM8994_HPOUT1L_DLY_MASK | WM8994_HPOUT1R_DLY_MASK, @@ -695,35 +750,49 @@ static int wm8994_device_init(struct wm8994_priv *wm8994)  	ret |= wm8994_update_bits(WM8994_OUTPUT_MIXER_2,  			WM8994_DAC1R_TO_HPOUT1R_MASK, WM8994_DAC1R_TO_HPOUT1R); -	/* Routing AIF2 to DAC1 */ -	ret |= wm8994_update_bits(WM8994_DAC1_LEFT_MIXER_ROUTING, -			WM8994_AIF2DACL_TO_DAC1L_MASK, -			WM8994_AIF2DACL_TO_DAC1L); +	if (aif_id == WM8994_AIF1) { +		/* Routing AIF1 to DAC1 */ +		ret |= wm8994_i2c_write(WM8994_DAC1_LEFT_MIXER_ROUTING, +				WM8994_AIF1DAC1L_TO_DAC1L); + +		ret |= wm8994_i2c_write(WM8994_DAC1_RIGHT_MIXER_ROUTING, +					WM8994_AIF1DAC1R_TO_DAC1R); + +		/* GPIO Settings for AIF1 */ +		ret |=  wm8994_i2c_write(WM8994_GPIO_1, WM8994_GPIO_DIR_OUTPUT +					 | WM8994_GPIO_FUNCTION_I2S_CLK +					 | WM8994_GPIO_INPUT_DEBOUNCE); -	ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_MIXER_ROUTING, -			WM8994_AIF2DACR_TO_DAC1R_MASK, -			WM8994_AIF2DACR_TO_DAC1R); +		ret |= wm8994_init_volume_aif1_dac1(); +	} else if (aif_id == WM8994_AIF2) { +		/* Routing AIF2 to DAC1 */ +		ret |= wm8994_update_bits(WM8994_DAC1_LEFT_MIXER_ROUTING, +				WM8994_AIF2DACL_TO_DAC1L_MASK, +				WM8994_AIF2DACL_TO_DAC1L); -	 /* GPIO Settings for AIF2 */ -	 /* B CLK */ -	ret |= wm8994_update_bits(WM8994_GPIO_3, WM8994_GPIO_DIR_MASK | -				WM8994_GPIO_FUNCTION_MASK , -				WM8994_GPIO_DIR_OUTPUT | -				WM8994_GPIO_FUNCTION_I2S_CLK); +		ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_MIXER_ROUTING, +				WM8994_AIF2DACR_TO_DAC1R_MASK, +				WM8994_AIF2DACR_TO_DAC1R); -	/* LR CLK */ -	ret |= wm8994_update_bits(WM8994_GPIO_4, WM8994_GPIO_DIR_MASK | -				WM8994_GPIO_FUNCTION_MASK, -				WM8994_GPIO_DIR_OUTPUT | -				WM8994_GPIO_FUNCTION_I2S_CLK); +		/* GPIO Settings for AIF2 */ +		/* B CLK */ +		ret |= wm8994_update_bits(WM8994_GPIO_3, WM8994_GPIO_DIR_MASK | +					WM8994_GPIO_FUNCTION_MASK , +					WM8994_GPIO_DIR_OUTPUT); -	/* DATA */ -	ret |= wm8994_update_bits(WM8994_GPIO_5, WM8994_GPIO_DIR_MASK | -				WM8994_GPIO_FUNCTION_MASK, -				WM8994_GPIO_DIR_OUTPUT | -				WM8994_GPIO_FUNCTION_I2S_CLK); +		/* LR CLK */ +		ret |= wm8994_update_bits(WM8994_GPIO_4, WM8994_GPIO_DIR_MASK | +					WM8994_GPIO_FUNCTION_MASK, +					WM8994_GPIO_DIR_OUTPUT); + +		/* DATA */ +		ret |= wm8994_update_bits(WM8994_GPIO_5, WM8994_GPIO_DIR_MASK | +					WM8994_GPIO_FUNCTION_MASK, +					WM8994_GPIO_DIR_OUTPUT); + +		ret |= wm8994_init_volume_aif2_dac1(); +	} -	ret |= wm8994_init_volume_aif2_dac1();  	if (ret < 0)  		goto err; @@ -795,7 +864,7 @@ static int get_codec_values(struct sound_codec_info *pcodec_info,  	return 0;  } -/*wm8994 Device Initialisation */ +/* WM8994 Device Initialisation */  int wm8994_init(const void *blob, enum en_audio_interface aif_id,  			int sampling_rate, int mclk_freq,  			int bits_per_sample, unsigned int channels) @@ -813,15 +882,15 @@ int wm8994_init(const void *blob, enum en_audio_interface aif_id,  	g_wm8994_i2c_dev_addr = pcodec_info->i2c_dev_addr;  	wm8994_i2c_init(pcodec_info->i2c_bus); -	if (pcodec_info->codec_type == CODEC_WM_8994) +	if (pcodec_info->codec_type == CODEC_WM_8994) {  		g_wm8994_info.type = WM8994; -	else { +	} else {  		debug("%s: Codec id [%d] not defined\n", __func__, -				pcodec_info->codec_type); +		      pcodec_info->codec_type);  		return -1;  	} -	ret = wm8994_device_init(&g_wm8994_info); +	ret = wm8994_device_init(&g_wm8994_info, aif_id);  	if (ret < 0) {  		debug("%s: wm8994 codec chip init failed\n", __func__);  		return ret; diff --git a/drivers/sound/wm8994_registers.h b/drivers/sound/wm8994_registers.h index 1e987c2fd..0aba2fdfd 100644 --- a/drivers/sound/wm8994_registers.h +++ b/drivers/sound/wm8994_registers.h @@ -13,6 +13,7 @@  #define WM8994_SOFTWARE_RESET                   0x00  #define WM8994_POWER_MANAGEMENT_1               0x01  #define WM8994_POWER_MANAGEMENT_2               0x02 +#define WM8994_POWER_MANAGEMENT_4		0x04  #define WM8994_POWER_MANAGEMENT_5               0x05  #define WM8994_LEFT_OUTPUT_VOLUME               0x1C  #define WM8994_RIGHT_OUTPUT_VOLUME              0x1D @@ -38,6 +39,7 @@  #define WM8994_AIF2_CONTROL_2                   0x311  #define WM8994_AIF2_MASTER_SLAVE                0x312  #define WM8994_AIF2_BCLK                        0x313 +#define WM8994_AIF1_DAC_FILTERS_1		0x420  #define WM8994_AIF2_DAC_LEFT_VOLUME             0x502  #define WM8994_AIF2_DAC_RIGHT_VOLUME            0x503  #define WM8994_AIF2_DAC_FILTERS_1               0x520 @@ -45,6 +47,7 @@  #define WM8994_DAC1_RIGHT_MIXER_ROUTING         0x602  #define WM8994_DAC1_LEFT_VOLUME                 0x610  #define WM8994_DAC1_RIGHT_VOLUME                0x611 +#define WM8994_GPIO_1				0x700  #define WM8994_GPIO_3                           0x702  #define WM8994_GPIO_4                           0x703  #define WM8994_GPIO_5                           0x704 @@ -82,6 +85,20 @@  /* OPCLK_ENA */  #define WM8994_OPCLK_ENA                        0x0800 +#define WM8994_TSHUT_ENA			0x4000 +#define WM8994_MIXINL_ENA			0x0200 +#define WM8994_MIXINR_ENA			0x0100 +#define WM8994_IN2L_ENA				0x0080 +#define WM8994_IN2R_ENA				0x0020 + +/* + * R5 (0x04) - Power Management (4) + */ +#define WM8994_ADCL_ENA				0x0001 +#define WM8994_ADCR_ENA				0x0002 +#define WM8994_AIF1ADC1R_ENA			0x0100 +#define WM8994_AIF1ADC1L_ENA			0x0200 +  /*   * R5 (0x05) - Power Management (5)   */ @@ -91,6 +108,12 @@  /* AIF2DACR_ENA */  #define WM8994_AIF2DACR_ENA                     0x1000  #define WM8994_AIF2DACR_ENA_MASK                0x1000 +/* AIF1DACL_ENA */ +#define WM8994_AIF1DACL_ENA			0x0200 +#define WM8994_AIF1DACL_ENA_MASK		0x0200 +/* AIF1DACR_ENA */ +#define WM8994_AIF1DACR_ENA			0x0100 +#define WM8994_AIF1DACR_ENA_MASK		0x0100  /* DAC1L_ENA */  #define WM8994_DAC1L_ENA                        0x0002  #define WM8994_DAC1L_ENA_MASK                   0x0002 @@ -170,6 +193,9 @@  /*   * R520 (0x208) - Clocking (1)   */ +/* AIF1DSPCLK_ENA */ +#define WM8994_AIF1DSPCLK_ENA			0x0008 +#define WM8994_AIF1DSPCLK_ENA_MASK		0x0008  /* AIF2DSPCLK_ENA */  #define WM8994_AIF2DSPCLK_ENA                   0x0004  #define WM8994_AIF2DSPCLK_ENA_MASK              0x0004 @@ -254,6 +280,8 @@  /* AIF2DACL_TO_DAC1L */  #define WM8994_AIF2DACL_TO_DAC1L                0x0004  #define WM8994_AIF2DACL_TO_DAC1L_MASK           0x0004 +/* AIF1DAC1L_TO_DAC1L */ +#define WM8994_AIF1DAC1L_TO_DAC1L		0x0001  /*   * R1538 (0x602) - DAC1 Right Mixer Routing @@ -261,6 +289,8 @@  /* AIF2DACR_TO_DAC1R */  #define WM8994_AIF2DACR_TO_DAC1R                0x0004  #define WM8994_AIF2DACR_TO_DAC1R_MASK           0x0004 +/* AIF1DAC1R_TO_DAC1R */ +#define WM8994_AIF1DAC1R_TO_DAC1R		0x0001  /*   * R1552 (0x610) - DAC1 Left Volume @@ -285,11 +315,12 @@   *  GPIO   */  /* OUTPUT PIN */ -#define WM8994_GPIO_DIR_OUTPUT                   0x8000 +#define WM8994_GPIO_DIR_OUTPUT			0x8000  /* GPIO PIN MASK */ -#define WM8994_GPIO_DIR_MASK                     0xFFE0 +#define WM8994_GPIO_DIR_MASK			0xFFE0  /* I2S CLK */ -#define WM8994_GPIO_FUNCTION_I2S_CLK             0x0000 +#define WM8994_GPIO_FUNCTION_I2S_CLK		0x0001 +#define WM8994_GPIO_INPUT_DEBOUNCE		0x0100  /* GPn FN */ -#define WM8994_GPIO_FUNCTION_MASK                0x001F +#define WM8994_GPIO_FUNCTION_MASK		0x001F  #endif diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 91d24cea5..e5941b09f 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -38,6 +38,7 @@ COBJS-$(CONFIG_FDT_SPI) += fdt_spi.o  COBJS-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o  COBJS-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.o  COBJS-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o +COBJS-$(CONFIG_TI_QSPI) += ti_qspi.o  COBJS-$(CONFIG_XILINX_SPI) += xilinx_spi.o  COBJS-$(CONFIG_ZYNQ_SPI) += zynq_spi.o diff --git a/drivers/spi/exynos_spi.c b/drivers/spi/exynos_spi.c index efc8b1e3a..699c57eb6 100644 --- a/drivers/spi/exynos_spi.c +++ b/drivers/spi/exynos_spi.c @@ -26,6 +26,7 @@ struct spi_bus {  	struct exynos_spi *regs;  	int inited;		/* 1 if this bus is ready for use */  	int node; +	uint deactivate_delay_us;	/* Delay to wait after deactivate */  };  /* A list of spi buses that we know about */ @@ -40,6 +41,8 @@ struct exynos_spi_slave {  	enum periph_id periph_id;	/* Peripheral ID for this device */  	unsigned int fifo_size;  	int skip_preamble; +	struct spi_bus *bus;		/* Pointer to our SPI bus info */ +	ulong last_transaction_us;	/* Time of last transaction end */  };  static struct spi_bus *spi_get_bus(unsigned dev_index) @@ -85,6 +88,7 @@ struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs,  	}  	bus = &spi_bus[busnum]; +	spi_slave->bus = bus;  	spi_slave->regs = bus->regs;  	spi_slave->mode = mode;  	spi_slave->periph_id = bus->periph_id; @@ -95,6 +99,7 @@ struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs,  		spi_slave->fifo_size = 256;  	spi_slave->skip_preamble = 0; +	spi_slave->last_transaction_us = timer_get_us();  	spi_slave->freq = bus->frequency;  	if (max_hz) @@ -199,12 +204,29 @@ static void spi_get_fifo_levels(struct exynos_spi *regs,   *   * @param regs	SPI peripheral registers   * @param count	Number of bytes to transfer + * @param step	Number of bytes to transfer in each packet (1 or 4)   */ -static void spi_request_bytes(struct exynos_spi *regs, int count) +static void spi_request_bytes(struct exynos_spi *regs, int count, int step)  { +	/* For word address we need to swap bytes */ +	if (step == 4) { +		setbits_le32(®s->mode_cfg, +			     SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD); +		count /= 4; +		setbits_le32(®s->swap_cfg, SPI_TX_SWAP_EN | SPI_RX_SWAP_EN | +			SPI_TX_BYTE_SWAP | SPI_RX_BYTE_SWAP | +			SPI_TX_HWORD_SWAP | SPI_RX_HWORD_SWAP); +	} else { +		/* Select byte access and clear the swap configuration */ +		clrbits_le32(®s->mode_cfg, +			     SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD); +		writel(0, ®s->swap_cfg); +	} +  	assert(count && count < (1 << 16));  	setbits_le32(®s->ch_cfg, SPI_CH_RST);  	clrbits_le32(®s->ch_cfg, SPI_CH_RST); +  	writel(count | SPI_PACKET_CNT_EN, ®s->pkt_cnt);  } @@ -219,6 +241,7 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,  	int toread;  	unsigned start = get_timer(0);  	int stopping; +	int step;  	out_bytes = in_bytes = todo; @@ -226,10 +249,19 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,  					!(spi_slave->mode & SPI_SLAVE);  	/* +	 * Try to transfer words if we can. This helps read performance at +	 * SPI clock speeds above about 20MHz. +	 */ +	step = 1; +	if (!((todo | (uintptr_t)rxp | (uintptr_t)txp) & 3) && +	    !spi_slave->skip_preamble) +		step = 4; + +	/*  	 * If there's something to send, do a software reset and set a  	 * transaction size.  	 */ -	spi_request_bytes(regs, todo); +	spi_request_bytes(regs, todo, step);  	/*  	 * Bytes are transmitted/received in pairs. Wait to receive all the @@ -242,24 +274,42 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,  		/* Keep the fifos full/empty. */  		spi_get_fifo_levels(regs, &rx_lvl, &tx_lvl); -		if (tx_lvl < spi_slave->fifo_size && out_bytes) { -			temp = txp ? *txp++ : 0xff; + +		/* +		 * Don't completely fill the txfifo, since we don't want our +		 * rxfifo to overflow, and it may already contain data. +		 */ +		while (tx_lvl < spi_slave->fifo_size/2 && out_bytes) { +			if (!txp) +				temp = -1; +			else if (step == 4) +				temp = *(uint32_t *)txp; +			else +				temp = *txp;  			writel(temp, ®s->tx_data); -			out_bytes--; +			out_bytes -= step; +			if (txp) +				txp += step; +			tx_lvl += step;  		} -		if (rx_lvl > 0) { -			temp = readl(®s->rx_data); -			if (spi_slave->skip_preamble) { -				if (temp == SPI_PREAMBLE_END_BYTE) { -					spi_slave->skip_preamble = 0; -					stopping = 0; +		if (rx_lvl >= step) { +			while (rx_lvl >= step) { +				temp = readl(®s->rx_data); +				if (spi_slave->skip_preamble) { +					if (temp == SPI_PREAMBLE_END_BYTE) { +						spi_slave->skip_preamble = 0; +						stopping = 0; +					} +				} else { +					if (rxp || stopping) { +						*rxp = temp; +						rxp += step; +					} +					in_bytes -= step;  				} -			} else { -				if (rxp || stopping) -					*rxp++ = temp; -				in_bytes--; +				toread -= step; +				rx_lvl -= step;  			} -			toread--;  		} else if (!toread) {  			/*  			 * We have run out of input data, but haven't read @@ -271,7 +321,7 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,  			out_bytes = in_bytes;  			toread = in_bytes;  			txp = NULL; -			spi_request_bytes(regs, toread); +			spi_request_bytes(regs, toread, step);  		}  		if (spi_slave->skip_preamble && get_timer(start) > 100) {  			printf("SPI timeout: in_bytes=%d, out_bytes=%d, ", @@ -315,10 +365,14 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,  	if ((flags & SPI_XFER_BEGIN))  		spi_cs_activate(slave); -	/* Exynos SPI limits each transfer to 65535 bytes */ +	/* +	 * Exynos SPI limits each transfer to 65535 transfers. To keep +	 * things simple, allow a maximum of 65532 bytes. We could allow +	 * more in word mode, but the performance difference is small. +	 */  	bytelen =  bitlen / 8;  	for (upto = 0; !ret && upto < bytelen; upto += todo) { -		todo = min(bytelen - upto, (1 << 16) - 1); +		todo = min(bytelen - upto, (1 << 16) - 4);  		ret = spi_rx_tx(spi_slave, todo, &din, &dout, flags);  		if (ret)  			break; @@ -359,9 +413,22 @@ void spi_cs_activate(struct spi_slave *slave)  {  	struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); +	/* If it's too soon to do another transaction, wait */ +	if (spi_slave->bus->deactivate_delay_us && +	    spi_slave->last_transaction_us) { +		ulong delay_us;		/* The delay completed so far */ +		delay_us = timer_get_us() - spi_slave->last_transaction_us; +		if (delay_us < spi_slave->bus->deactivate_delay_us) +			udelay(spi_slave->bus->deactivate_delay_us - delay_us); +	} +  	clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT);  	debug("Activate CS, bus %d\n", spi_slave->slave.bus);  	spi_slave->skip_preamble = spi_slave->mode & SPI_PREAMBLE; + +	/* Remember time of this transaction so we can honour the bus delay */ +	if (spi_slave->bus->deactivate_delay_us) +		spi_slave->last_transaction_us = timer_get_us();  }  /** @@ -411,6 +478,8 @@ static int spi_get_config(const void *blob, int node, struct spi_bus *bus)  	/* Use 500KHz as a suitable default */  	bus->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",  					500000); +	bus->deactivate_delay_us = fdtdec_get_int(blob, node, +					"spi-deactivate-delay", 0);  	return 0;  } diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c index 3cf7142f5..2b9f395a9 100644 --- a/drivers/spi/mxs_spi.c +++ b/drivers/spi/mxs_spi.c @@ -56,8 +56,6 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  				  unsigned int max_hz, unsigned int mode)  {  	struct mxs_spi_slave *mxs_slave; -	struct mxs_ssp_regs *ssp_regs; -	int reg;  	if (!spi_cs_is_valid(bus, cs)) {  		printf("mxs_spi: invalid bus %d / chip select %d\n", bus, cs); @@ -74,13 +72,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  	mxs_slave->max_khz = max_hz / 1000;  	mxs_slave->mode = mode;  	mxs_slave->regs = mxs_ssp_regs_by_bus(bus); -	ssp_regs = mxs_slave->regs; -	reg = readl(&ssp_regs->hw_ssp_ctrl0); -	reg &= ~(MXS_SSP_CHIPSELECT_MASK); -	reg |= cs << MXS_SSP_CHIPSELECT_SHIFT; - -	writel(reg, &ssp_regs->hw_ssp_ctrl0);  	return &mxs_slave->slave;  err_init: @@ -102,7 +94,9 @@ int spi_claim_bus(struct spi_slave *slave)  	mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg); -	writel(SSP_CTRL0_BUS_WIDTH_ONE_BIT, &ssp_regs->hw_ssp_ctrl0); +	writel((slave->cs << MXS_SSP_CHIPSELECT_SHIFT) | +	       SSP_CTRL0_BUS_WIDTH_ONE_BIT, +	       &ssp_regs->hw_ssp_ctrl0);  	reg = SSP_CTRL1_SSP_MODE_SPI | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS;  	reg |= (mxs_slave->mode & SPI_CPOL) ? SSP_CTRL1_POLARITY : 0; diff --git a/drivers/spi/ti_qspi.c b/drivers/spi/ti_qspi.c new file mode 100644 index 000000000..5a5b48276 --- /dev/null +++ b/drivers/spi/ti_qspi.c @@ -0,0 +1,311 @@ +/* + * TI QSPI driver + * + * Copyright (C) 2013, Texas Instruments, Incorporated + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/omap.h> +#include <malloc.h> +#include <spi.h> + +/* ti qpsi register bit masks */ +#define QSPI_TIMEOUT                    2000000 +#define QSPI_FCLK                       192000000 +/* clock control */ +#define QSPI_CLK_EN                     (1 << 31) +#define QSPI_CLK_DIV_MAX                0xffff +/* command */ +#define QSPI_EN_CS(n)                   (n << 28) +#define QSPI_WLEN(n)                    ((n-1) << 19) +#define QSPI_3_PIN                      (1 << 18) +#define QSPI_RD_SNGL                    (1 << 16) +#define QSPI_WR_SNGL                    (2 << 16) +#define QSPI_INVAL                      (4 << 16) +#define QSPI_RD_QUAD                    (7 << 16) +/* device control */ +#define QSPI_DD(m, n)                   (m << (3 + n*8)) +#define QSPI_CKPHA(n)                   (1 << (2 + n*8)) +#define QSPI_CSPOL(n)                   (1 << (1 + n*8)) +#define QSPI_CKPOL(n)                   (1 << (n*8)) +/* status */ +#define QSPI_WC                         (1 << 1) +#define QSPI_BUSY                       (1 << 0) +#define QSPI_WC_BUSY                    (QSPI_WC | QSPI_BUSY) +#define QSPI_XFER_DONE                  QSPI_WC +#define MM_SWITCH                       0x01 +#define MEM_CS                          0x100 +#define MEM_CS_UNSELECT                 0xfffff0ff +#define MMAP_START_ADDR                 0x5c000000 +#define CORE_CTRL_IO                    0x4a002558 + +#define QSPI_CMD_READ                   (0x3 << 0) +#define QSPI_CMD_READ_QUAD              (0x6b << 0) +#define QSPI_CMD_READ_FAST              (0x0b << 0) +#define QSPI_SETUP0_NUM_A_BYTES         (0x2 << 8) +#define QSPI_SETUP0_NUM_D_BYTES_NO_BITS (0x0 << 10) +#define QSPI_SETUP0_NUM_D_BYTES_8_BITS  (0x1 << 10) +#define QSPI_SETUP0_READ_NORMAL         (0x0 << 12) +#define QSPI_SETUP0_READ_QUAD           (0x3 << 12) +#define QSPI_CMD_WRITE                  (0x2 << 16) +#define QSPI_NUM_DUMMY_BITS             (0x0 << 24) + +/* ti qspi register set */ +struct ti_qspi_regs { +	u32 pid; +	u32 pad0[3]; +	u32 sysconfig; +	u32 pad1[3]; +	u32 int_stat_raw; +	u32 int_stat_en; +	u32 int_en_set; +	u32 int_en_ctlr; +	u32 intc_eoi; +	u32 pad2[3]; +	u32 clk_ctrl; +	u32 dc; +	u32 cmd; +	u32 status; +	u32 data; +	u32 setup0; +	u32 setup1; +	u32 setup2; +	u32 setup3; +	u32 memswitch; +	u32 data1; +	u32 data2; +	u32 data3; +}; + +/* ti qspi slave */ +struct ti_qspi_slave { +	struct spi_slave slave; +	struct ti_qspi_regs *base; +	unsigned int mode; +	u32 cmd; +	u32 dc; +}; + +static inline struct ti_qspi_slave *to_ti_qspi_slave(struct spi_slave *slave) +{ +	return container_of(slave, struct ti_qspi_slave, slave); +} + +static void ti_spi_setup_spi_register(struct ti_qspi_slave *qslave) +{ +	struct spi_slave *slave = &qslave->slave; +	u32 memval = 0; + +	slave->memory_map = (void *)MMAP_START_ADDR; + +	memval |= QSPI_CMD_READ | QSPI_SETUP0_NUM_A_BYTES | +			QSPI_SETUP0_NUM_D_BYTES_NO_BITS | +			QSPI_SETUP0_READ_NORMAL | QSPI_CMD_WRITE | +			QSPI_NUM_DUMMY_BITS; + +	writel(memval, &qslave->base->setup0); +} + +static void ti_spi_set_speed(struct spi_slave *slave, uint hz) +{ +	struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); +	uint clk_div; + +	debug("ti_spi_set_speed: hz: %d, clock divider %d\n", hz, clk_div); + +	if (!hz) +		clk_div = 0; +	else +		clk_div = (QSPI_FCLK / hz) - 1; + +	/* disable SCLK */ +	writel(readl(&qslave->base->clk_ctrl) & ~QSPI_CLK_EN, +	       &qslave->base->clk_ctrl); + +	/* assign clk_div values */ +	if (clk_div < 0) +		clk_div = 0; +	else if (clk_div > QSPI_CLK_DIV_MAX) +		clk_div = QSPI_CLK_DIV_MAX; + +	/* enable SCLK */ +	writel(QSPI_CLK_EN | clk_div, &qslave->base->clk_ctrl); +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ +	return 1; +} + +void spi_cs_activate(struct spi_slave *slave) +{ +	/* CS handled in xfer */ +	return; +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ +	struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); + +	debug("spi_cs_deactivate: 0x%08x\n", (u32)slave); + +	writel(qslave->cmd | QSPI_INVAL, &qslave->base->cmd); +} + +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) +{ +	struct ti_qspi_slave *qslave; + +	qslave = spi_alloc_slave(struct ti_qspi_slave, bus, cs); +	if (!qslave) { +		printf("SPI_error: Fail to allocate ti_qspi_slave\n"); +		return NULL; +	} + +	qslave->base = (struct ti_qspi_regs *)QSPI_BASE; +	qslave->mode = mode; + +	ti_spi_set_speed(&qslave->slave, max_hz); + +#ifdef CONFIG_TI_SPI_MMAP +	ti_spi_setup_spi_register(qslave); +#endif + +	return &qslave->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ +	struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); +	free(qslave); +} + +int spi_claim_bus(struct spi_slave *slave) +{ +	struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); + +	debug("spi_claim_bus: bus:%i cs:%i\n", slave->bus, slave->cs); + +	qslave->dc = 0; +	if (qslave->mode & SPI_CPHA) +		qslave->dc |= QSPI_CKPHA(slave->cs); +	if (qslave->mode & SPI_CPOL) +		qslave->dc |= QSPI_CKPOL(slave->cs); +	if (qslave->mode & SPI_CS_HIGH) +		qslave->dc |= QSPI_CSPOL(slave->cs); + +	writel(qslave->dc, &qslave->base->dc); +	writel(0, &qslave->base->cmd); +	writel(0, &qslave->base->data); + +	return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ +	struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); + +	debug("spi_release_bus: bus:%i cs:%i\n", slave->bus, slave->cs); + +	writel(0, &qslave->base->dc); +	writel(0, &qslave->base->cmd); +	writel(0, &qslave->base->data); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, +	     void *din, unsigned long flags) +{ +	struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); +	uint words = bitlen >> 3; /* fixed 8-bit word length */ +	const uchar *txp = dout; +	uchar *rxp = din; +	uint status; +	int timeout, val; + +	debug("spi_xfer: bus:%i cs:%i bitlen:%i words:%i flags:%lx\n", +	      slave->bus, slave->cs, bitlen, words, flags); + +	/* Setup mmap flags */ +	if (flags & SPI_XFER_MMAP) { +		writel(MM_SWITCH, &qslave->base->memswitch); +		val = readl(CORE_CTRL_IO); +		val |= MEM_CS; +		writel(val, CORE_CTRL_IO); +		return 0; +	} else if (flags & SPI_XFER_MMAP_END) { +		writel(~MM_SWITCH, &qslave->base->memswitch); +		val = readl(CORE_CTRL_IO); +		val &= MEM_CS_UNSELECT; +		writel(val, CORE_CTRL_IO); +		return 0; +	} + +	if (bitlen == 0) +		return -1; + +	if (bitlen % 8) { +		debug("spi_xfer: Non byte aligned SPI transfer\n"); +		return -1; +	} + +	/* Setup command reg */ +	qslave->cmd = 0; +	qslave->cmd |= QSPI_WLEN(8); +	qslave->cmd |= QSPI_EN_CS(slave->cs); +	if (flags & SPI_3WIRE) +		qslave->cmd |= QSPI_3_PIN; +	qslave->cmd |= 0xfff; + +	while (words--) { +		if (txp) { +			debug("tx cmd %08x dc %08x data %02x\n", +			      qslave->cmd | QSPI_WR_SNGL, qslave->dc, *txp); +			writel(*txp++, &qslave->base->data); +			writel(qslave->cmd | QSPI_WR_SNGL, +			       &qslave->base->cmd); +			status = readl(&qslave->base->status); +			timeout = QSPI_TIMEOUT; +			while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) { +				if (--timeout < 0) { +					printf("spi_xfer: TX timeout!\n"); +					return -1; +				} +				status = readl(&qslave->base->status); +			} +			debug("tx done, status %08x\n", status); +		} +		if (rxp) { +			qslave->cmd |= QSPI_RD_SNGL; +			debug("rx cmd %08x dc %08x\n", +			      qslave->cmd, qslave->dc); +			writel(qslave->cmd, &qslave->base->cmd); +			status = readl(&qslave->base->status); +			timeout = QSPI_TIMEOUT; +			while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) { +				if (--timeout < 0) { +					printf("spi_xfer: RX timeout!\n"); +					return -1; +				} +				status = readl(&qslave->base->status); +			} +			*rxp++ = readl(&qslave->base->data); +			debug("rx done, status %08x, read %02x\n", +			      status, *(rxp-1)); +		} +	} + +	/* Terminate frame */ +	if (flags & SPI_XFER_END) +		spi_cs_deactivate(slave); + +	return 0; +} diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 4c2a39a21..1590c4a75 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -18,10 +18,12 @@ endif  # new USB gadget layer dependencies  ifdef CONFIG_USB_GADGET +COBJS-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_udc.o  COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o  COBJS-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o  COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o  COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o +COBJS-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o  endif  ifdef CONFIG_USB_ETHER  COBJS-y += ether.o diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c new file mode 100644 index 000000000..c99208d10 --- /dev/null +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -0,0 +1,1306 @@ +/* + * Driver for the Atmel USBA high speed USB device controller + * [Original from Linux kernel: drivers/usb/gadget/atmel_usba_udc.c] + * + * Copyright (C) 2005-2013 Atmel Corporation + *			   Bo Shen <voice.shen@atmel.com> + * + * SPDX-License-Identifier:     GPL-2.0+ + */ + +#include <common.h> +#include <asm/errno.h> +#include <asm/gpio.h> +#include <asm/hardware.h> +#include <linux/list.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/atmel_usba_udc.h> +#include <malloc.h> +#include <usb/lin_gadget_compat.h> + +#include "atmel_usba_udc.h" + +static int vbus_is_present(struct usba_udc *udc) +{ +	/* No Vbus detection: Assume always present */ +	return 1; +} + +static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req) +{ +	unsigned int transaction_len; + +	transaction_len = req->req.length - req->req.actual; +	req->last_transaction = 1; +	if (transaction_len > ep->ep.maxpacket) { +		transaction_len = ep->ep.maxpacket; +		req->last_transaction = 0; +	} else if (transaction_len == ep->ep.maxpacket && req->req.zero) { +			req->last_transaction = 0; +	} + +	DBG(DBG_QUEUE, "%s: submit_transaction, req %p (length %d)%s\n", +	    ep->ep.name, req, transaction_len, +	    req->last_transaction ? ", done" : ""); + +	memcpy(ep->fifo, req->req.buf + req->req.actual, transaction_len); +	usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); +	req->req.actual += transaction_len; +} + +static void submit_request(struct usba_ep *ep, struct usba_request *req) +{ +	DBG(DBG_QUEUE, "%s: submit_request: req %p (length %d), dma: %d\n", +	    ep->ep.name, req, req->req.length, req->using_dma); + +	req->req.actual = 0; +	req->submitted = 1; + +	next_fifo_transaction(ep, req); +	if (req->last_transaction) { +		usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY); +		usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE); +	} else { +		usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); +		usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY); +	} +} + +static void submit_next_request(struct usba_ep *ep) +{ +	struct usba_request *req; + +	if (list_empty(&ep->queue)) { +		usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY | USBA_RX_BK_RDY); +		return; +	} + +	req = list_entry(ep->queue.next, struct usba_request, queue); +	if (!req->submitted) +		submit_request(ep, req); +} + +static void send_status(struct usba_udc *udc, struct usba_ep *ep) +{ +	ep->state = STATUS_STAGE_IN; +	usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); +	usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE); +} + +static void receive_data(struct usba_ep *ep) +{ +	struct usba_udc *udc = ep->udc; +	struct usba_request *req; +	unsigned long status; +	unsigned int bytecount, nr_busy; +	int is_complete = 0; + +	status = usba_ep_readl(ep, STA); +	nr_busy = USBA_BFEXT(BUSY_BANKS, status); + +	DBG(DBG_QUEUE, "receive data: nr_busy=%u\n", nr_busy); + +	while (nr_busy > 0) { +		if (list_empty(&ep->queue)) { +			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); +			break; +		} +		req = list_entry(ep->queue.next, +				 struct usba_request, queue); + +		bytecount = USBA_BFEXT(BYTE_COUNT, status); + +		if (status & USBA_SHORT_PACKET) +			is_complete = 1; +		if (req->req.actual + bytecount >= req->req.length) { +			is_complete = 1; +			bytecount = req->req.length - req->req.actual; +		} + +		memcpy(req->req.buf + req->req.actual, ep->fifo, bytecount); +		req->req.actual += bytecount; + +		usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); + +		if (is_complete) { +			DBG(DBG_QUEUE, "%s: request done\n", ep->ep.name); +			req->req.status = 0; +			list_del_init(&req->queue); +			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); +			spin_lock(&udc->lock); +			req->req.complete(&ep->ep, &req->req); +			spin_unlock(&udc->lock); +		} + +		status = usba_ep_readl(ep, STA); +		nr_busy = USBA_BFEXT(BUSY_BANKS, status); + +		if (is_complete && ep_is_control(ep)) { +			send_status(udc, ep); +			break; +		} +	} +} + +static void +request_complete(struct usba_ep *ep, struct usba_request *req, int status) +{ +	if (req->req.status == -EINPROGRESS) +		req->req.status = status; + +	DBG(DBG_GADGET | DBG_REQ, "%s: req %p complete: status %d, actual %u\n", +	    ep->ep.name, req, req->req.status, req->req.actual); + +	req->req.complete(&ep->ep, &req->req); +} + +static void +request_complete_list(struct usba_ep *ep, struct list_head *list, int status) +{ +	struct usba_request *req, *tmp_req; + +	list_for_each_entry_safe(req, tmp_req, list, queue) { +		list_del_init(&req->queue); +		request_complete(ep, req, status); +	} +} + +static int +usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ +	struct usba_ep *ep = to_usba_ep(_ep); +	struct usba_udc *udc = ep->udc; +	unsigned long flags, ept_cfg, maxpacket; +	unsigned int nr_trans; + +	DBG(DBG_GADGET, "%s: ep_enable: desc=%p\n", ep->ep.name, desc); + +	maxpacket = usb_endpoint_maxp(desc) & 0x7ff; + +	if (((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) +	      != ep->index) || +	      ep->index == 0 || +	      desc->bDescriptorType != USB_DT_ENDPOINT || +	      maxpacket == 0 || +	      maxpacket > ep->fifo_size) { +		DBG(DBG_ERR, "ep_enable: Invalid argument"); +		return -EINVAL; +	} + +	ep->is_isoc = 0; +	ep->is_in = 0; + +	if (maxpacket <= 8) +		ept_cfg = USBA_BF(EPT_SIZE, USBA_EPT_SIZE_8); +	else +		/* LSB is bit 1, not 0 */ +		ept_cfg = USBA_BF(EPT_SIZE, fls(maxpacket - 1) - 3); + +	DBG(DBG_HW, "%s: EPT_SIZE = %lu (maxpacket = %lu)\n", +	    ep->ep.name, ept_cfg, maxpacket); + +	if (usb_endpoint_dir_in(desc)) { +		ep->is_in = 1; +		ept_cfg |= USBA_EPT_DIR_IN; +	} + +	switch (usb_endpoint_type(desc)) { +	case USB_ENDPOINT_XFER_CONTROL: +		ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL); +		ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE); +		break; +	case USB_ENDPOINT_XFER_ISOC: +		if (!ep->can_isoc) { +			DBG(DBG_ERR, "ep_enable: %s is not isoc capable\n", +			    ep->ep.name); +			return -EINVAL; +		} + +		/* +		 * Bits 11:12 specify number of _additional_ +		 * transactions per microframe. +		 */ +		nr_trans = ((usb_endpoint_maxp(desc) >> 11) & 3) + 1; +		if (nr_trans > 3) +			return -EINVAL; + +		ep->is_isoc = 1; +		ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_ISO); + +		/* +		 * Do triple-buffering on high-bandwidth iso endpoints. +		 */ +		if (nr_trans > 1 && ep->nr_banks == 3) +			ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_TRIPLE); +		else +			ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_DOUBLE); +		ept_cfg |= USBA_BF(NB_TRANS, nr_trans); +		break; +	case USB_ENDPOINT_XFER_BULK: +		ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK); +		ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE); +		break; +	case USB_ENDPOINT_XFER_INT: +		ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_INT); +		ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE); +		break; +	} + +	spin_lock_irqsave(&ep->udc->lock, flags); + +	ep->desc = desc; +	ep->ep.maxpacket = maxpacket; + +	usba_ep_writel(ep, CFG, ept_cfg); +	usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE); + +	usba_writel(udc, INT_ENB, +		    (usba_readl(udc, INT_ENB) +		     | USBA_BF(EPT_INT, 1 << ep->index))); + +	spin_unlock_irqrestore(&udc->lock, flags); + +	DBG(DBG_HW, "EPT_CFG%d after init: %#08lx\n", ep->index, +	    (unsigned long)usba_ep_readl(ep, CFG)); +	DBG(DBG_HW, "INT_ENB after init: %#08lx\n", +	    (unsigned long)usba_readl(udc, INT_ENB)); + +	return 0; +} + +static int usba_ep_disable(struct usb_ep *_ep) +{ +	struct usba_ep *ep = to_usba_ep(_ep); +	struct usba_udc *udc = ep->udc; +	LIST_HEAD(req_list); +	unsigned long flags; + +	DBG(DBG_GADGET, "ep_disable: %s\n", ep->ep.name); + +	spin_lock_irqsave(&udc->lock, flags); + +	if (!ep->desc) { +		spin_unlock_irqrestore(&udc->lock, flags); +		/* REVISIT because this driver disables endpoints in +		 * reset_all_endpoints() before calling disconnect(), +		 * most gadget drivers would trigger this non-error ... +		 */ +		if (udc->gadget.speed != USB_SPEED_UNKNOWN) +			DBG(DBG_ERR, "ep_disable: %s not enabled\n", +			    ep->ep.name); +		return -EINVAL; +	} +	ep->desc = NULL; + +	list_splice_init(&ep->queue, &req_list); +	usba_ep_writel(ep, CFG, 0); +	usba_ep_writel(ep, CTL_DIS, USBA_EPT_ENABLE); +	usba_writel(udc, INT_ENB, +		    usba_readl(udc, INT_ENB) & +		    ~USBA_BF(EPT_INT, 1 << ep->index)); + +	request_complete_list(ep, &req_list, -ESHUTDOWN); + +	spin_unlock_irqrestore(&udc->lock, flags); + +	return 0; +} + +static struct usb_request * +usba_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ +	struct usba_request *req; + +	DBG(DBG_GADGET, "ep_alloc_request: %p, 0x%x\n", _ep, gfp_flags); + +	req = malloc(sizeof(struct usba_request)); +	if (!req) +		return NULL; + +	INIT_LIST_HEAD(&req->queue); + +	return &req->req; +} + +static void +usba_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ +	struct usba_request *req = to_usba_req(_req); + +	DBG(DBG_GADGET, "ep_free_request: %p, %p\n", _ep, _req); + +	free(req); +} + +static int +usba_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ +	struct usba_request *req = to_usba_req(_req); +	struct usba_ep *ep = to_usba_ep(_ep); +	struct usba_udc *udc = ep->udc; +	unsigned long flags; +	int ret; + +	DBG(DBG_GADGET | DBG_QUEUE | DBG_REQ, "%s: queue req %p, len %u\n", +	    ep->ep.name, req, _req->length); + +	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN || +	    !ep->desc) +		return -ESHUTDOWN; + +	req->submitted = 0; +	req->using_dma = 0; +	req->last_transaction = 0; + +	_req->status = -EINPROGRESS; +	_req->actual = 0; + +	/* May have received a reset since last time we checked */ +	ret = -ESHUTDOWN; +	spin_lock_irqsave(&udc->lock, flags); +	if (ep->desc) { +		list_add_tail(&req->queue, &ep->queue); + +		if ((!ep_is_control(ep) && ep->is_in) || +		    (ep_is_control(ep) && (ep->state == DATA_STAGE_IN || +		    ep->state == STATUS_STAGE_IN))) +			usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY); +		else +			usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY); + +		ret = 0; +	} +	spin_unlock_irqrestore(&udc->lock, flags); + +	return ret; +} + +static int usba_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ +	struct usba_ep *ep = to_usba_ep(_ep); +	struct usba_request *req = to_usba_req(_req); + +	DBG(DBG_GADGET | DBG_QUEUE, "ep_dequeue: %s, req %p\n", +	    ep->ep.name, req); + +	/* +	 * Errors should stop the queue from advancing until the +	 * completion function returns. +	 */ +	list_del_init(&req->queue); + +	request_complete(ep, req, -ECONNRESET); + +	/* Process the next request if any */ +	submit_next_request(ep); + +	return 0; +} + +static int usba_ep_set_halt(struct usb_ep *_ep, int value) +{ +	struct usba_ep *ep = to_usba_ep(_ep); +	unsigned long flags; +	int ret = 0; + +	DBG(DBG_GADGET, "endpoint %s: %s HALT\n", ep->ep.name, +	    value ? "set" : "clear"); + +	if (!ep->desc) { +		DBG(DBG_ERR, "Attempted to halt uninitialized ep %s\n", +		    ep->ep.name); +		return -ENODEV; +	} + +	if (ep->is_isoc) { +		DBG(DBG_ERR, "Attempted to halt isochronous ep %s\n", +		    ep->ep.name); +		return -ENOTTY; +	} + +	spin_lock_irqsave(&udc->lock, flags); + +	/* +	 * We can't halt IN endpoints while there are still data to be +	 * transferred +	 */ +	if (!list_empty(&ep->queue) || +	    ((value && ep->is_in && (usba_ep_readl(ep, STA) & +	    USBA_BF(BUSY_BANKS, -1L))))) { +		ret = -EAGAIN; +	} else { +		if (value) +			usba_ep_writel(ep, SET_STA, USBA_FORCE_STALL); +		else +			usba_ep_writel(ep, CLR_STA, +				       USBA_FORCE_STALL | USBA_TOGGLE_CLR); +		usba_ep_readl(ep, STA); +	} + +	spin_unlock_irqrestore(&udc->lock, flags); + +	return ret; +} + +static int usba_ep_fifo_status(struct usb_ep *_ep) +{ +	struct usba_ep *ep = to_usba_ep(_ep); + +	return USBA_BFEXT(BYTE_COUNT, usba_ep_readl(ep, STA)); +} + +static void usba_ep_fifo_flush(struct usb_ep *_ep) +{ +	struct usba_ep *ep = to_usba_ep(_ep); +	struct usba_udc *udc = ep->udc; + +	usba_writel(udc, EPT_RST, 1 << ep->index); +} + +static const struct usb_ep_ops usba_ep_ops = { +	.enable		= usba_ep_enable, +	.disable	= usba_ep_disable, +	.alloc_request	= usba_ep_alloc_request, +	.free_request	= usba_ep_free_request, +	.queue		= usba_ep_queue, +	.dequeue	= usba_ep_dequeue, +	.set_halt	= usba_ep_set_halt, +	.fifo_status	= usba_ep_fifo_status, +	.fifo_flush	= usba_ep_fifo_flush, +}; + +static int usba_udc_get_frame(struct usb_gadget *gadget) +{ +	struct usba_udc *udc = to_usba_udc(gadget); + +	return USBA_BFEXT(FRAME_NUMBER, usba_readl(udc, FNUM)); +} + +static int usba_udc_wakeup(struct usb_gadget *gadget) +{ +	struct usba_udc *udc = to_usba_udc(gadget); +	unsigned long flags; +	u32 ctrl; +	int ret = -EINVAL; + +	spin_lock_irqsave(&udc->lock, flags); +	if (udc->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) { +		ctrl = usba_readl(udc, CTRL); +		usba_writel(udc, CTRL, ctrl | USBA_REMOTE_WAKE_UP); +		ret = 0; +	} +	spin_unlock_irqrestore(&udc->lock, flags); + +	return ret; +} + +static int +usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) +{ +	struct usba_udc *udc = to_usba_udc(gadget); +	unsigned long flags; + +	spin_lock_irqsave(&udc->lock, flags); +	if (is_selfpowered) +		udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED; +	else +		udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); +	spin_unlock_irqrestore(&udc->lock, flags); + +	return 0; +} + +static const struct usb_gadget_ops usba_udc_ops = { +	.get_frame		= usba_udc_get_frame, +	.wakeup			= usba_udc_wakeup, +	.set_selfpowered	= usba_udc_set_selfpowered, +}; + +static struct usb_endpoint_descriptor usba_ep0_desc = { +	.bLength = USB_DT_ENDPOINT_SIZE, +	.bDescriptorType = USB_DT_ENDPOINT, +	.bEndpointAddress = 0, +	.bmAttributes = USB_ENDPOINT_XFER_CONTROL, +	.wMaxPacketSize = cpu_to_le16(64), +	/* FIXME: I have no idea what to put here */ +	.bInterval = 1, +}; + +/* + * Called with interrupts disabled and udc->lock held. + */ +static void reset_all_endpoints(struct usba_udc *udc) +{ +	struct usba_ep *ep; +	struct usba_request *req, *tmp_req; + +	usba_writel(udc, EPT_RST, ~0UL); + +	ep = to_usba_ep(udc->gadget.ep0); +	list_for_each_entry_safe(req, tmp_req, &ep->queue, queue) { +		list_del_init(&req->queue); +		request_complete(ep, req, -ECONNRESET); +	} + +	/* NOTE:  normally, the next call to the gadget driver is in +	 * charge of disabling endpoints... usually disconnect(). +	 * The exception would be entering a high speed test mode. +	 * +	 * FIXME remove this code ... and retest thoroughly. +	 */ +	list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { +		if (ep->desc) { +			spin_unlock(&udc->lock); +			usba_ep_disable(&ep->ep); +			spin_lock(&udc->lock); +		} +	} +} + +static struct usba_ep *get_ep_by_addr(struct usba_udc *udc, u16 wIndex) +{ +	struct usba_ep *ep; + +	if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) +		return to_usba_ep(udc->gadget.ep0); + +	list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { +		u8 bEndpointAddress; + +		if (!ep->desc) +			continue; +		bEndpointAddress = ep->desc->bEndpointAddress; +		if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) +			continue; +		if ((bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) +				== (wIndex & USB_ENDPOINT_NUMBER_MASK)) +			return ep; +	} + +	return NULL; +} + +/* Called with interrupts disabled and udc->lock held */ +static inline void set_protocol_stall(struct usba_udc *udc, struct usba_ep *ep) +{ +	usba_ep_writel(ep, SET_STA, USBA_FORCE_STALL); +	ep->state = WAIT_FOR_SETUP; +} + +static inline int is_stalled(struct usba_udc *udc, struct usba_ep *ep) +{ +	if (usba_ep_readl(ep, STA) & USBA_FORCE_STALL) +		return 1; +	return 0; +} + +static inline void set_address(struct usba_udc *udc, unsigned int addr) +{ +	u32 regval; + +	DBG(DBG_BUS, "setting address %u...\n", addr); +	regval = usba_readl(udc, CTRL); +	regval = USBA_BFINS(DEV_ADDR, addr, regval); +	usba_writel(udc, CTRL, regval); +} + +static int do_test_mode(struct usba_udc *udc) +{ +	static const char test_packet_buffer[] = { +		/* JKJKJKJK * 9 */ +		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +		/* JJKKJJKK * 8 */ +		0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, +		/* JJKKJJKK * 8 */ +		0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, +		/* JJJJJJJKKKKKKK * 8 */ +		0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +		/* JJJJJJJK * 8 */ +		0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, +		/* {JKKKKKKK * 10}, JK */ +		0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0x7E +	}; +	struct usba_ep *ep; +	int test_mode; + +	test_mode = udc->test_mode; + +	/* Start from a clean slate */ +	reset_all_endpoints(udc); + +	switch (test_mode) { +	case 0x0100: +		/* Test_J */ +		usba_writel(udc, TST, USBA_TST_J_MODE); +		DBG(DBG_ALL, "Entering Test_J mode...\n"); +		break; +	case 0x0200: +		/* Test_K */ +		usba_writel(udc, TST, USBA_TST_K_MODE); +		DBG(DBG_ALL, "Entering Test_K mode...\n"); +		break; +	case 0x0300: +		/* +		 * Test_SE0_NAK: Force high-speed mode and set up ep0 +		 * for Bulk IN transfers +		 */ +		ep = &udc->usba_ep[0]; +		usba_writel(udc, TST, +			    USBA_BF(SPEED_CFG, USBA_SPEED_CFG_FORCE_HIGH)); +		usba_ep_writel(ep, CFG, +			       USBA_BF(EPT_SIZE, USBA_EPT_SIZE_64) +			       | USBA_EPT_DIR_IN +			       | USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK) +			       | USBA_BF(BK_NUMBER, 1)); +		if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) { +			set_protocol_stall(udc, ep); +			DBG(DBG_ALL, "Test_SE0_NAK: ep0 not mapped\n"); +		} else { +			usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE); +			DBG(DBG_ALL, "Entering Test_SE0_NAK mode...\n"); +		} +		break; +	case 0x0400: +		/* Test_Packet */ +		ep = &udc->usba_ep[0]; +		usba_ep_writel(ep, CFG, +			       USBA_BF(EPT_SIZE, USBA_EPT_SIZE_64) +			       | USBA_EPT_DIR_IN +			       | USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK) +			       | USBA_BF(BK_NUMBER, 1)); +		if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) { +			set_protocol_stall(udc, ep); +			DBG(DBG_ALL, "Test_Packet: ep0 not mapped\n"); +		} else { +			usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE); +			usba_writel(udc, TST, USBA_TST_PKT_MODE); +			memcpy(ep->fifo, test_packet_buffer, +			       sizeof(test_packet_buffer)); +			usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); +			DBG(DBG_ALL, "Entering Test_Packet mode...\n"); +		} +		break; +	default: +		DBG(DBG_ERR, "Invalid test mode: 0x%04x\n", test_mode); +		return -EINVAL; +	} + +	return 0; +} + +/* Avoid overly long expressions */ +static inline bool feature_is_dev_remote_wakeup(struct usb_ctrlrequest *crq) +{ +	if (crq->wValue == cpu_to_le16(USB_DEVICE_REMOTE_WAKEUP)) +		return true; +	return false; +} + +static inline bool feature_is_dev_test_mode(struct usb_ctrlrequest *crq) +{ +	if (crq->wValue == cpu_to_le16(USB_DEVICE_TEST_MODE)) +		return true; +	return false; +} + +static inline bool feature_is_ep_halt(struct usb_ctrlrequest *crq) +{ +	if (crq->wValue == cpu_to_le16(USB_ENDPOINT_HALT)) +		return true; +	return false; +} + +static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep, +		struct usb_ctrlrequest *crq) +{ +	int retval = 0; + +	switch (crq->bRequest) { +	case USB_REQ_GET_STATUS: { +		u16 status; + +		if (crq->bRequestType == (USB_DIR_IN | USB_RECIP_DEVICE)) { +			status = cpu_to_le16(udc->devstatus); +		} else if (crq->bRequestType +				== (USB_DIR_IN | USB_RECIP_INTERFACE)) { +			status = cpu_to_le16(0); +		} else if (crq->bRequestType +				== (USB_DIR_IN | USB_RECIP_ENDPOINT)) { +			struct usba_ep *target; + +			target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex)); +			if (!target) +				goto stall; + +			status = 0; +			if (is_stalled(udc, target)) +				status |= cpu_to_le16(1); +		} else { +			goto delegate; +		} + +		/* Write directly to the FIFO. No queueing is done. */ +		if (crq->wLength != cpu_to_le16(sizeof(status))) +			goto stall; +		ep->state = DATA_STAGE_IN; +		__raw_writew(status, ep->fifo); +		usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); +		break; +	} + +	case USB_REQ_CLEAR_FEATURE: { +		if (crq->bRequestType == USB_RECIP_DEVICE) { +			if (feature_is_dev_remote_wakeup(crq)) +				udc->devstatus +					&= ~(1 << USB_DEVICE_REMOTE_WAKEUP); +			else +				/* Can't CLEAR_FEATURE TEST_MODE */ +				goto stall; +		} else if (crq->bRequestType == USB_RECIP_ENDPOINT) { +			struct usba_ep *target; + +			if (crq->wLength != cpu_to_le16(0) || +			    !feature_is_ep_halt(crq)) +				goto stall; +			target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex)); +			if (!target) +				goto stall; + +			usba_ep_writel(target, CLR_STA, USBA_FORCE_STALL); +			if (target->index != 0) +				usba_ep_writel(target, CLR_STA, +					       USBA_TOGGLE_CLR); +		} else { +			goto delegate; +		} + +		send_status(udc, ep); +		break; +	} + +	case USB_REQ_SET_FEATURE: { +		if (crq->bRequestType == USB_RECIP_DEVICE) { +			if (feature_is_dev_test_mode(crq)) { +				send_status(udc, ep); +				ep->state = STATUS_STAGE_TEST; +				udc->test_mode = le16_to_cpu(crq->wIndex); +				return 0; +			} else if (feature_is_dev_remote_wakeup(crq)) { +				udc->devstatus |= 1 << USB_DEVICE_REMOTE_WAKEUP; +			} else { +				goto stall; +			} +		} else if (crq->bRequestType == USB_RECIP_ENDPOINT) { +			struct usba_ep *target; + +			if (crq->wLength != cpu_to_le16(0) || +			    !feature_is_ep_halt(crq)) +				goto stall; + +			target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex)); +			if (!target) +				goto stall; + +			usba_ep_writel(target, SET_STA, USBA_FORCE_STALL); +		} else { +			goto delegate; +		} + +		send_status(udc, ep); +		break; +	} + +	case USB_REQ_SET_ADDRESS: +		if (crq->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE)) +			goto delegate; + +		set_address(udc, le16_to_cpu(crq->wValue)); +		send_status(udc, ep); +		ep->state = STATUS_STAGE_ADDR; +		break; + +	default: +delegate: +		spin_unlock(&udc->lock); +		retval = udc->driver->setup(&udc->gadget, crq); +		spin_lock(&udc->lock); +	} + +	return retval; + +stall: +	DBG(DBG_ALL, "%s: Invalid setup request: %02x.%02x v%04x i%04x l%d\n", +	    ep->ep.name, crq->bRequestType, crq->bRequest, +	    le16_to_cpu(crq->wValue), le16_to_cpu(crq->wIndex), +	    le16_to_cpu(crq->wLength)); +	set_protocol_stall(udc, ep); + +	return -1; +} + +static void usba_control_irq(struct usba_udc *udc, struct usba_ep *ep) +{ +	struct usba_request *req; +	u32 epstatus; +	u32 epctrl; + +restart: +	epstatus = usba_ep_readl(ep, STA); +	epctrl = usba_ep_readl(ep, CTL); + +	DBG(DBG_INT, "%s [%d]: s/%08x c/%08x\n", +	    ep->ep.name, ep->state, epstatus, epctrl); + +	req = NULL; +	if (!list_empty(&ep->queue)) +		req = list_entry(ep->queue.next, +				 struct usba_request, queue); + +	if ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) { +		if (req->submitted) +			next_fifo_transaction(ep, req); +		else +			submit_request(ep, req); + +		if (req->last_transaction) { +			usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY); +			usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE); +		} +		goto restart; +	} +	if ((epstatus & epctrl) & USBA_TX_COMPLETE) { +		usba_ep_writel(ep, CLR_STA, USBA_TX_COMPLETE); + +		switch (ep->state) { +		case DATA_STAGE_IN: +			usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY); +			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); +			ep->state = STATUS_STAGE_OUT; +			break; +		case STATUS_STAGE_ADDR: +			/* Activate our new address */ +			usba_writel(udc, CTRL, (usba_readl(udc, CTRL) +						| USBA_FADDR_EN)); +			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); +			ep->state = WAIT_FOR_SETUP; +			break; +		case STATUS_STAGE_IN: +			if (req) { +				list_del_init(&req->queue); +				request_complete(ep, req, 0); +				submit_next_request(ep); +			} +			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); +			ep->state = WAIT_FOR_SETUP; +			break; +		case STATUS_STAGE_TEST: +			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); +			ep->state = WAIT_FOR_SETUP; +			if (do_test_mode(udc)) +				set_protocol_stall(udc, ep); +			break; +		default: +			DBG(DBG_ALL, "%s: TXCOMP: Invalid endpoint state %d\n", +			    ep->ep.name, ep->state); +			set_protocol_stall(udc, ep); +			break; +		} + +		goto restart; +	} +	if ((epstatus & epctrl) & USBA_RX_BK_RDY) { +		switch (ep->state) { +		case STATUS_STAGE_OUT: +			usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); +			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); + +			if (req) { +				list_del_init(&req->queue); +				request_complete(ep, req, 0); +			} +			ep->state = WAIT_FOR_SETUP; +			break; + +		case DATA_STAGE_OUT: +			receive_data(ep); +			break; + +		default: +			usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); +			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); +			DBG(DBG_ALL, "%s: RXRDY: Invalid endpoint state %d\n", +			    ep->ep.name, ep->state); +			set_protocol_stall(udc, ep); +			break; +		} + +		goto restart; +	} +	if (epstatus & USBA_RX_SETUP) { +		union { +			struct usb_ctrlrequest crq; +			unsigned long data[2]; +		} crq; +		unsigned int pkt_len; +		int ret; + +		if (ep->state != WAIT_FOR_SETUP) { +			/* +			 * Didn't expect a SETUP packet at this +			 * point. Clean up any pending requests (which +			 * may be successful). +			 */ +			int status = -EPROTO; + +			/* +			 * RXRDY and TXCOMP are dropped when SETUP +			 * packets arrive.  Just pretend we received +			 * the status packet. +			 */ +			if (ep->state == STATUS_STAGE_OUT || +			    ep->state == STATUS_STAGE_IN) { +				usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY); +				status = 0; +			} + +			if (req) { +				list_del_init(&req->queue); +				request_complete(ep, req, status); +			} +		} + +		pkt_len = USBA_BFEXT(BYTE_COUNT, usba_ep_readl(ep, STA)); +		DBG(DBG_HW, "Packet length: %u\n", pkt_len); +		if (pkt_len != sizeof(crq)) { +			DBG(DBG_ALL, "udc: Invalid length %u (expected %zu)\n", +			    pkt_len, sizeof(crq)); +			set_protocol_stall(udc, ep); +			return; +		} + +		DBG(DBG_FIFO, "Copying ctrl request from 0x%p:\n", ep->fifo); +		memcpy(crq.data, ep->fifo, sizeof(crq)); + +		/* Free up one bank in the FIFO so that we can +		 * generate or receive a reply right away. */ +		usba_ep_writel(ep, CLR_STA, USBA_RX_SETUP); + +		if (crq.crq.bRequestType & USB_DIR_IN) { +			/* +			 * The USB 2.0 spec states that "if wLength is +			 * zero, there is no data transfer phase." +			 * However, testusb #14 seems to actually +			 * expect a data phase even if wLength = 0... +			 */ +			ep->state = DATA_STAGE_IN; +		} else { +			if (crq.crq.wLength != cpu_to_le16(0)) +				ep->state = DATA_STAGE_OUT; +			else +				ep->state = STATUS_STAGE_IN; +		} + +		ret = -1; +		if (ep->index == 0) { +			ret = handle_ep0_setup(udc, ep, &crq.crq); +		} else { +			spin_unlock(&udc->lock); +			ret = udc->driver->setup(&udc->gadget, &crq.crq); +			spin_lock(&udc->lock); +		} + +		DBG(DBG_BUS, "req %02x.%02x, length %d, state %d, ret %d\n", +		    crq.crq.bRequestType, crq.crq.bRequest, +		    le16_to_cpu(crq.crq.wLength), ep->state, ret); + +		if (ret < 0) { +			/* Let the host know that we failed */ +			set_protocol_stall(udc, ep); +		} +	} +} + +static void usba_ep_irq(struct usba_udc *udc, struct usba_ep *ep) +{ +	struct usba_request *req; +	u32 epstatus; +	u32 epctrl; + +	epstatus = usba_ep_readl(ep, STA); +	epctrl = usba_ep_readl(ep, CTL); + +	DBG(DBG_INT, "%s: interrupt, status: 0x%08x\n", ep->ep.name, epstatus); + +	while ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) { +		DBG(DBG_BUS, "%s: TX PK ready\n", ep->ep.name); + +		if (list_empty(&ep->queue)) { +			DBG(DBG_INT, "ep_irq: queue empty\n"); +			usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY); +			return; +		} + +		req = list_entry(ep->queue.next, struct usba_request, queue); + +		if (req->submitted) +			next_fifo_transaction(ep, req); +		else +			submit_request(ep, req); + +		if (req->last_transaction) { +			list_del_init(&req->queue); +			submit_next_request(ep); +			request_complete(ep, req, 0); +		} + +		epstatus = usba_ep_readl(ep, STA); +		epctrl = usba_ep_readl(ep, CTL); +	} + +	if ((epstatus & epctrl) & USBA_RX_BK_RDY) { +		DBG(DBG_BUS, "%s: RX data ready\n", ep->ep.name); +		receive_data(ep); +		usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); +	} +} + +static int usba_udc_irq(struct usba_udc *udc) +{ +	u32 status, ep_status; + +	spin_lock(&udc->lock); + +	status = usba_readl(udc, INT_STA); +	DBG(DBG_INT, "irq, status=%#08x\n", status); + +	if (status & USBA_DET_SUSPEND) { +		usba_writel(udc, INT_CLR, USBA_DET_SUSPEND); +		DBG(DBG_BUS, "Suspend detected\n"); +		if (udc->gadget.speed != USB_SPEED_UNKNOWN && +		    udc->driver && udc->driver->suspend) { +			spin_unlock(&udc->lock); +			udc->driver->suspend(&udc->gadget); +			spin_lock(&udc->lock); +		} +	} + +	if (status & USBA_WAKE_UP) { +		usba_writel(udc, INT_CLR, USBA_WAKE_UP); +		DBG(DBG_BUS, "Wake Up CPU detected\n"); +	} + +	if (status & USBA_END_OF_RESUME) { +		usba_writel(udc, INT_CLR, USBA_END_OF_RESUME); +		DBG(DBG_BUS, "Resume detected\n"); +		if (udc->gadget.speed != USB_SPEED_UNKNOWN && +		    udc->driver && udc->driver->resume) { +			spin_unlock(&udc->lock); +			udc->driver->resume(&udc->gadget); +			spin_lock(&udc->lock); +		} +	} + +	ep_status = USBA_BFEXT(EPT_INT, status); +	if (ep_status) { +		int i; + +		for (i = 0; i < USBA_NR_ENDPOINTS; i++) +			if (ep_status & (1 << i)) { +				if (ep_is_control(&udc->usba_ep[i])) +					usba_control_irq(udc, &udc->usba_ep[i]); +				else +					usba_ep_irq(udc, &udc->usba_ep[i]); +			} +	} + +	if (status & USBA_END_OF_RESET) { +		struct usba_ep *ep0; + +		usba_writel(udc, INT_CLR, USBA_END_OF_RESET); +		reset_all_endpoints(udc); + +		if (udc->gadget.speed != USB_SPEED_UNKNOWN && +		    udc->driver->disconnect) { +			udc->gadget.speed = USB_SPEED_UNKNOWN; +			spin_unlock(&udc->lock); +			udc->driver->disconnect(&udc->gadget); +			spin_lock(&udc->lock); +		} + +		if (status & USBA_HIGH_SPEED) +			udc->gadget.speed = USB_SPEED_HIGH; +		else +			udc->gadget.speed = USB_SPEED_FULL; + +		ep0 = &udc->usba_ep[0]; +		ep0->desc = &usba_ep0_desc; +		ep0->state = WAIT_FOR_SETUP; +		usba_ep_writel(ep0, CFG, +			       (USBA_BF(EPT_SIZE, EP0_EPT_SIZE) +				| USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL) +				| USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE))); +		usba_ep_writel(ep0, CTL_ENB, +			       USBA_EPT_ENABLE | USBA_RX_SETUP); +		usba_writel(udc, INT_ENB, +			    (usba_readl(udc, INT_ENB) +			     | USBA_BF(EPT_INT, 1) +			     | USBA_DET_SUSPEND +			     | USBA_END_OF_RESUME)); + +		/* +		 * Unclear why we hit this irregularly, e.g. in usbtest, +		 * but it's clearly harmless... +		 */ +		if (!(usba_ep_readl(ep0, CFG) & USBA_EPT_MAPPED)) +			DBG(DBG_ALL, "ODD: EP0 configuration is invalid!\n"); +	} + +	spin_unlock(&udc->lock); + +	return 0; +} + +static int atmel_usba_start(struct usba_udc *udc) +{ +	udc->devstatus = 1 << USB_DEVICE_SELF_POWERED; + +	udc->vbus_prev = 0; + +	/* If Vbus is present, enable the controller and wait for reset */ +	if (vbus_is_present(udc) && udc->vbus_prev == 0) { +		usba_writel(udc, CTRL, USBA_ENABLE_MASK); +		usba_writel(udc, INT_ENB, USBA_END_OF_RESET); +	} + +	return 0; +} + +static int atmel_usba_stop(struct usba_udc *udc) +{ +	udc->gadget.speed = USB_SPEED_UNKNOWN; +	reset_all_endpoints(udc); + +	/* This will also disable the DP pullup */ +	usba_writel(udc, CTRL, USBA_DISABLE_MASK); + +	return 0; +} + +static struct usba_udc controller = { +	.regs = (unsigned *)ATMEL_BASE_UDPHS, +	.fifo = (unsigned *)ATMEL_BASE_UDPHS_FIFO, +	.gadget = { +		.ops		= &usba_udc_ops, +		.ep_list	= LIST_HEAD_INIT(controller.gadget.ep_list), +		.speed		= USB_SPEED_HIGH, +		.is_dualspeed	= 1, +		.name		= "atmel_usba_udc", +	}, +}; + +int usb_gadget_handle_interrupts(void) +{ +	struct usba_udc *udc = &controller; + +	return usba_udc_irq(udc); +} + + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ +	struct usba_udc *udc = &controller; +	int ret; + +	if (!driver || !driver->bind || !driver->setup) { +		printf("bad paramter\n"); +		return -EINVAL; +	} + +	if (udc->driver) { +		printf("UDC already has a gadget driver\n"); +		return -EBUSY; +	} + +	atmel_usba_start(udc); + +	udc->driver = driver; + +	ret = driver->bind(&udc->gadget); +	if (ret) { +		error("driver->bind() returned %d\n", ret); +		udc->driver = NULL; +	} + +	return ret; +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ +	struct usba_udc *udc = &controller; + +	if (!driver || !driver->unbind || !driver->disconnect) { +		error("bad paramter\n"); +		return -EINVAL; +	} + +	driver->disconnect(&udc->gadget); +	driver->unbind(&udc->gadget); +	udc->driver = NULL; + +	atmel_usba_stop(udc); + +	return 0; +} + +static struct usba_ep *usba_udc_pdata(struct usba_platform_data *pdata, +				      struct usba_udc *udc) +{ +	struct usba_ep *eps; +	int i; + +	eps = malloc(sizeof(struct usba_ep) * pdata->num_ep); +	if (!eps) { +		error("failed to alloc eps\n"); +		return NULL; +	} + +	udc->gadget.ep0 = &eps[0].ep; + +	INIT_LIST_HEAD(&udc->gadget.ep_list); +	INIT_LIST_HEAD(&eps[0].ep.ep_list); + +	for (i = 0; i < pdata->num_ep; i++) { +		struct usba_ep *ep = &eps[i]; + +		ep->ep_regs = udc->regs + USBA_EPT_BASE(i); +		ep->dma_regs = udc->regs + USBA_DMA_BASE(i); +		ep->fifo = udc->fifo + USBA_FIFO_BASE(i); +		ep->ep.ops = &usba_ep_ops; +		ep->ep.name = pdata->ep[i].name; +		ep->ep.maxpacket = pdata->ep[i].fifo_size; +		ep->fifo_size = ep->ep.maxpacket; +		ep->udc = udc; +		INIT_LIST_HEAD(&ep->queue); +		ep->nr_banks = pdata->ep[i].nr_banks; +		ep->index = pdata->ep[i].index; +		ep->can_dma = pdata->ep[i].can_dma; +		ep->can_isoc = pdata->ep[i].can_isoc; +		if (i) +			list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); +	}; + +	return eps; +} + +int usba_udc_probe(struct usba_platform_data *pdata) +{ +	struct usba_udc *udc; + +	udc = &controller; + +	udc->usba_ep = usba_udc_pdata(pdata, udc); + +	return 0; +} diff --git a/drivers/usb/gadget/atmel_usba_udc.h b/drivers/usb/gadget/atmel_usba_udc.h new file mode 100644 index 000000000..92e462db6 --- /dev/null +++ b/drivers/usb/gadget/atmel_usba_udc.h @@ -0,0 +1,326 @@ +/* + * Register definition for Atmel USBA high speed USB device controller + * [Original from Linux kernel: drivers/usb/gadget/atmel_usba_udc.h] + * + * Copyright (C) 2005-2013 Atmel Corporation + *			   Bo Shen <voice.shen@atmel.com> + * + * SPDX-License-Identifier:     GPL-2.0+ + */ + +#ifndef __LINUX_USB_GADGET_USBA_UDC_H__ +#define __LINUX_USB_GADGET_USBA_UDC_H__ + +/* USB register offsets */ +#define USBA_CTRL				0x0000 +#define USBA_FNUM				0x0004 +#define USBA_INT_ENB				0x0010 +#define USBA_INT_STA				0x0014 +#define USBA_INT_CLR				0x0018 +#define USBA_EPT_RST				0x001c +#define USBA_TST				0x00e0 + +/* USB endpoint register offsets */ +#define USBA_EPT_CFG				0x0000 +#define USBA_EPT_CTL_ENB			0x0004 +#define USBA_EPT_CTL_DIS			0x0008 +#define USBA_EPT_CTL				0x000c +#define USBA_EPT_SET_STA			0x0014 +#define USBA_EPT_CLR_STA			0x0018 +#define USBA_EPT_STA				0x001c + +/* USB DMA register offsets */ +#define USBA_DMA_NXT_DSC			0x0000 +#define USBA_DMA_ADDRESS			0x0004 +#define USBA_DMA_CONTROL			0x0008 +#define USBA_DMA_STATUS				0x000c + +/* Bitfields in CTRL */ +#define USBA_DEV_ADDR_OFFSET			0 +#define USBA_DEV_ADDR_SIZE			7 +#define USBA_FADDR_EN				(1 <<  7) +#define USBA_EN_USBA				(1 <<  8) +#define USBA_DETACH				(1 <<  9) +#define USBA_REMOTE_WAKE_UP			(1 << 10) +#define USBA_PULLD_DIS				(1 << 11) + +#if defined(CONFIG_AVR32) +#define USBA_ENABLE_MASK			USBA_EN_USBA +#define USBA_DISABLE_MASK			0 +#elif defined(CONFIG_AT91FAMILY) +#define USBA_ENABLE_MASK			(USBA_EN_USBA | USBA_PULLD_DIS) +#define USBA_DISABLE_MASK			USBA_DETACH +#endif /* CONFIG_ARCH_AT91 */ + +/* Bitfields in FNUM */ +#define USBA_MICRO_FRAME_NUM_OFFSET		0 +#define USBA_MICRO_FRAME_NUM_SIZE		3 +#define USBA_FRAME_NUMBER_OFFSET		3 +#define USBA_FRAME_NUMBER_SIZE			11 +#define USBA_FRAME_NUM_ERROR			(1 << 31) + +/* Bitfields in INT_ENB/INT_STA/INT_CLR */ +#define USBA_HIGH_SPEED				(1 <<  0) +#define USBA_DET_SUSPEND			(1 <<  1) +#define USBA_MICRO_SOF				(1 <<  2) +#define USBA_SOF				(1 <<  3) +#define USBA_END_OF_RESET			(1 <<  4) +#define USBA_WAKE_UP				(1 <<  5) +#define USBA_END_OF_RESUME			(1 <<  6) +#define USBA_UPSTREAM_RESUME			(1 <<  7) +#define USBA_EPT_INT_OFFSET			8 +#define USBA_EPT_INT_SIZE			16 +#define USBA_DMA_INT_OFFSET			24 +#define USBA_DMA_INT_SIZE			8 + +/* Bitfields in EPT_RST */ +#define USBA_RST_OFFSET				0 +#define USBA_RST_SIZE				16 + +/* Bitfields in USBA_TST */ +#define USBA_SPEED_CFG_OFFSET			0 +#define USBA_SPEED_CFG_SIZE			2 +#define USBA_TST_J_MODE				(1 <<  2) +#define USBA_TST_K_MODE				(1 <<  3) +#define USBA_TST_PKT_MODE			(1 <<  4) +#define USBA_OPMODE2				(1 <<  5) + +/* Bitfields in EPT_CFG */ +#define USBA_EPT_SIZE_OFFSET			0 +#define USBA_EPT_SIZE_SIZE			3 +#define USBA_EPT_DIR_IN				(1 <<  3) +#define USBA_EPT_TYPE_OFFSET			4 +#define USBA_EPT_TYPE_SIZE			2 +#define USBA_BK_NUMBER_OFFSET			6 +#define USBA_BK_NUMBER_SIZE			2 +#define USBA_NB_TRANS_OFFSET			8 +#define USBA_NB_TRANS_SIZE			2 +#define USBA_EPT_MAPPED				(1 << 31) + +/* Bitfields in EPT_CTL/EPT_CTL_ENB/EPT_CTL_DIS */ +#define USBA_EPT_ENABLE				(1 <<  0) +#define USBA_AUTO_VALID				(1 <<  1) +#define USBA_INTDIS_DMA				(1 <<  3) +#define USBA_NYET_DIS				(1 <<  4) +#define USBA_DATAX_RX				(1 <<  6) +#define USBA_MDATA_RX				(1 <<  7) +/* Bits 8-15 and 31 enable interrupts for respective bits in EPT_STA */ +#define USBA_BUSY_BANK_IE			(1 << 18) + +/* Bitfields in EPT_SET_STA/EPT_CLR_STA/EPT_STA */ +#define USBA_FORCE_STALL			(1 <<  5) +#define USBA_TOGGLE_CLR				(1 <<  6) +#define USBA_TOGGLE_SEQ_OFFSET			6 +#define USBA_TOGGLE_SEQ_SIZE			2 +#define USBA_ERR_OVFLW				(1 <<  8) +#define USBA_RX_BK_RDY				(1 <<  9) +#define USBA_KILL_BANK				(1 <<  9) +#define USBA_TX_COMPLETE			(1 << 10) +#define USBA_TX_PK_RDY				(1 << 11) +#define USBA_ISO_ERR_TRANS			(1 << 11) +#define USBA_RX_SETUP				(1 << 12) +#define USBA_ISO_ERR_FLOW			(1 << 12) +#define USBA_STALL_SENT				(1 << 13) +#define USBA_ISO_ERR_CRC			(1 << 13) +#define USBA_ISO_ERR_NBTRANS			(1 << 13) +#define USBA_NAK_IN				(1 << 14) +#define USBA_ISO_ERR_FLUSH			(1 << 14) +#define USBA_NAK_OUT				(1 << 15) +#define USBA_CURRENT_BANK_OFFSET		16 +#define USBA_CURRENT_BANK_SIZE			2 +#define USBA_BUSY_BANKS_OFFSET			18 +#define USBA_BUSY_BANKS_SIZE			2 +#define USBA_BYTE_COUNT_OFFSET			20 +#define USBA_BYTE_COUNT_SIZE			11 +#define USBA_SHORT_PACKET			(1 << 31) + +/* Bitfields in DMA_CONTROL */ +#define USBA_DMA_CH_EN				(1 <<  0) +#define USBA_DMA_LINK				(1 <<  1) +#define USBA_DMA_END_TR_EN			(1 <<  2) +#define USBA_DMA_END_BUF_EN			(1 <<  3) +#define USBA_DMA_END_TR_IE			(1 <<  4) +#define USBA_DMA_END_BUF_IE			(1 <<  5) +#define USBA_DMA_DESC_LOAD_IE			(1 <<  6) +#define USBA_DMA_BURST_LOCK			(1 <<  7) +#define USBA_DMA_BUF_LEN_OFFSET			16 +#define USBA_DMA_BUF_LEN_SIZE			16 + +/* Bitfields in DMA_STATUS */ +#define USBA_DMA_CH_ACTIVE			(1 <<  1) +#define USBA_DMA_END_TR_ST			(1 <<  4) +#define USBA_DMA_END_BUF_ST			(1 <<  5) +#define USBA_DMA_DESC_LOAD_ST			(1 <<  6) + +/* Constants for SPEED_CFG */ +#define USBA_SPEED_CFG_NORMAL			0 +#define USBA_SPEED_CFG_FORCE_HIGH		2 +#define USBA_SPEED_CFG_FORCE_FULL		3 + +/* Constants for EPT_SIZE */ +#define USBA_EPT_SIZE_8				0 +#define USBA_EPT_SIZE_16			1 +#define USBA_EPT_SIZE_32			2 +#define USBA_EPT_SIZE_64			3 +#define USBA_EPT_SIZE_128			4 +#define USBA_EPT_SIZE_256			5 +#define USBA_EPT_SIZE_512			6 +#define USBA_EPT_SIZE_1024			7 + +/* Constants for EPT_TYPE */ +#define USBA_EPT_TYPE_CONTROL			0 +#define USBA_EPT_TYPE_ISO			1 +#define USBA_EPT_TYPE_BULK			2 +#define USBA_EPT_TYPE_INT			3 + +/* Constants for BK_NUMBER */ +#define USBA_BK_NUMBER_ZERO			0 +#define USBA_BK_NUMBER_ONE			1 +#define USBA_BK_NUMBER_DOUBLE			2 +#define USBA_BK_NUMBER_TRIPLE			3 + +/* Bit manipulation macros */ +#define USBA_BF(name, value)					\ +	(((value) & ((1 << USBA_##name##_SIZE) - 1))		\ +	 << USBA_##name##_OFFSET) +#define USBA_BFEXT(name, value)					\ +	(((value) >> USBA_##name##_OFFSET)			\ +	 & ((1 << USBA_##name##_SIZE) - 1)) +#define USBA_BFINS(name, value, old)				\ +	(((old) & ~(((1 << USBA_##name##_SIZE) - 1)		\ +		    << USBA_##name##_OFFSET))			\ +	 | USBA_BF(name, value)) + +/* Register access macros */ +#define usba_readl(udc, reg)					\ +	__raw_readl((udc)->regs + USBA_##reg) +#define usba_writel(udc, reg, value)				\ +	__raw_writel((value), (udc)->regs + USBA_##reg) +#define usba_ep_readl(ep, reg)					\ +	__raw_readl((ep)->ep_regs + USBA_EPT_##reg) +#define usba_ep_writel(ep, reg, value)				\ +	__raw_writel((value), (ep)->ep_regs + USBA_EPT_##reg) +#define usba_dma_readl(ep, reg)					\ +	__raw_readl((ep)->dma_regs + USBA_DMA_##reg) +#define usba_dma_writel(ep, reg, value)				\ +	__raw_writel((value), (ep)->dma_regs + USBA_DMA_##reg) + +/* Calculate base address for a given endpoint or DMA controller */ +#define USBA_EPT_BASE(x)	(0x100 + (x) * 0x20) +#define USBA_DMA_BASE(x)	(0x300 + (x) * 0x10) +#define USBA_FIFO_BASE(x)	((x) << 16) + +/* Synth parameters */ +#define USBA_NR_ENDPOINTS	7 + +#define EP0_FIFO_SIZE		64 +#define EP0_EPT_SIZE		USBA_EPT_SIZE_64 +#define EP0_NR_BANKS		1 + +#define DBG_ERR		0x0001	/* report all error returns */ +#define DBG_HW		0x0002	/* debug hardware initialization */ +#define DBG_GADGET	0x0004	/* calls to/from gadget driver */ +#define DBG_INT		0x0008	/* interrupts */ +#define DBG_BUS		0x0010	/* report changes in bus state */ +#define DBG_QUEUE	0x0020  /* debug request queue processing */ +#define DBG_FIFO	0x0040  /* debug FIFO contents */ +#define DBG_DMA		0x0080  /* debug DMA handling */ +#define DBG_REQ		0x0100	/* print out queued request length */ +#define DBG_ALL		0xffff +#define DBG_NONE	0x0000 + +#define DEBUG_LEVEL	(DBG_ERR) + +#define DBG(level, fmt, ...)					\ +	do {							\ +		if ((level) & DEBUG_LEVEL)			\ +			debug("udc: " fmt, ## __VA_ARGS__);	\ +	} while (0) + +enum usba_ctrl_state { +	WAIT_FOR_SETUP, +	DATA_STAGE_IN, +	DATA_STAGE_OUT, +	STATUS_STAGE_IN, +	STATUS_STAGE_OUT, +	STATUS_STAGE_ADDR, +	STATUS_STAGE_TEST, +}; + +struct usba_dma_desc { +	dma_addr_t next; +	dma_addr_t addr; +	u32 ctrl; +}; + +struct usba_ep { +	int					state; +	void					*ep_regs; +	void					*dma_regs; +	void					*fifo; +	struct usb_ep				ep; +	struct usba_udc				*udc; + +	struct list_head			queue; + +	u16					fifo_size; +	u8					nr_banks; +	u8					index; +	unsigned int				can_dma:1; +	unsigned int				can_isoc:1; +	unsigned int				is_isoc:1; +	unsigned int				is_in:1; + +	const struct usb_endpoint_descriptor	*desc; +}; + +struct usba_request { +	struct usb_request			req; +	struct list_head			queue; + +	u32					ctrl; + +	unsigned int				submitted:1; +	unsigned int				last_transaction:1; +	unsigned int				using_dma:1; +	unsigned int				mapped:1; +}; + +struct usba_udc { +	void *regs; +	void *fifo; + +	struct usb_gadget gadget; +	struct usb_gadget_driver *driver; +	struct platform_device *pdev; +	int irq; +	int vbus_pin; +	int vbus_pin_inverted; +	int num_ep; +	struct usba_ep *usba_ep; + +	u16 devstatus; + +	u16 test_mode; +	int vbus_prev; +}; + +static inline struct usba_ep *to_usba_ep(struct usb_ep *ep) +{ +	return container_of(ep, struct usba_ep, ep); +} + +static inline struct usba_request *to_usba_req(struct usb_request *req) +{ +	return container_of(req, struct usba_request, req); +} + +static inline struct usba_udc *to_usba_udc(struct usb_gadget *gadget) +{ +	return container_of(gadget, struct usba_udc, gadget); +} + +#define ep_is_control(ep)	((ep)->index == 0) +#define ep_is_idle(ep)		((ep)->state == EP_STATE_IDLE) + +#endif /* __LINUX_USB_GADGET_USBA_UDC_H */ diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index f563afe78..014a6791c 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -10,6 +10,7 @@   */  #include <common.h> +#include <asm/unaligned.h>  #include <asm/errno.h>  #include <linux/list.h>  #include <linux/string.h> @@ -86,7 +87,8 @@ int usb_gadget_config_buf(  	/* config descriptor first */  	if (length < USB_DT_CONFIG_SIZE || !desc)  		return -EINVAL; -	*cp = *config; +	/* config need not be aligned */ +	memcpy(cp, config, sizeof(*cp));  	/* then interface/endpoint/class/vendor/... */  	len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8 *)buf, @@ -100,7 +102,7 @@ int usb_gadget_config_buf(  	/* patch up the config descriptor */  	cp->bLength = USB_DT_CONFIG_SIZE;  	cp->bDescriptorType = USB_DT_CONFIG; -	cp->wTotalLength = cpu_to_le16(len); +	put_unaligned_le16(len, &cp->wTotalLength);  	cp->bmAttributes |= USB_CONFIG_ATT_ONE;  	return len;  } diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 579893cbf..700d5fbfb 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -849,9 +849,10 @@ static struct usb_gadget_strings	stringtab = {  };  /*============================================================================*/ -static u8 control_req[USB_BUFSIZ]; +DEFINE_CACHE_ALIGN_BUFFER(u8, control_req, USB_BUFSIZ); +  #if defined(CONFIG_USB_ETH_CDC) || defined(CONFIG_USB_ETH_RNDIS) -static u8 status_req[STATUS_BYTECOUNT] __attribute__ ((aligned(4))); +DEFINE_CACHE_ALIGN_BUFFER(u8, status_req, STATUS_BYTECOUNT);  #endif diff --git a/drivers/usb/gadget/f_dfu.h b/drivers/usb/gadget/f_dfu.h index 34a4dde5a..cc2c45567 100644 --- a/drivers/usb/gadget/f_dfu.h +++ b/drivers/usb/gadget/f_dfu.h @@ -82,7 +82,4 @@ struct dfu_function_descriptor {  	__le16				wTransferSize;  	__le16				bcdDFUVersion;  } __packed; - -/* configuration-specific linkup */ -int dfu_add(struct usb_configuration *c);  #endif /* __F_DFU_H_ */ diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index a3e05a872..40868c034 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/drivers/usb/gadget/g_dnl.c @@ -7,7 +7,6 @@   * SPDX-License-Identifier:	GPL-2.0+   */ -#include <errno.h>  #include <common.h>  #include <malloc.h> @@ -15,11 +14,11 @@  #include <part.h>  #include <g_dnl.h> -#include "f_dfu.h" +#include <usb_mass_storage.h> +#include <dfu.h>  #include "gadget_chips.h"  #include "composite.c" -#include "f_mass_storage.c"  /*   * One needs to define the following: diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index fdad73972..3ae04c025 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -36,6 +36,12 @@  #define CONFIG_USB_MAX_CONTROLLER_COUNT 1  #endif +/* + * EHCI spec page 20 says that the HC may take up to 16 uFrames (= 4ms) to halt. + * Let's time out after 8 to have a little safety margin on top of that. + */ +#define HCHALT_TIMEOUT (8 * 1000) +  static struct ehci_ctrl ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT];  #define ALIGN_END_ADDR(type, ptr, size)			\ @@ -190,6 +196,36 @@ out:  	return ret;  } +static int ehci_shutdown(struct ehci_ctrl *ctrl) +{ +	int i, ret = 0; +	uint32_t cmd, reg; + +	cmd = ehci_readl(&ctrl->hcor->or_usbcmd); +	cmd &= ~(CMD_PSE | CMD_ASE); +	ehci_writel(&ctrl->hcor->or_usbcmd, cmd); +	ret = handshake(&ctrl->hcor->or_usbsts, STS_ASS | STS_PSS, 0, +		100 * 1000); + +	if (!ret) { +		for (i = 0; i < CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS; i++) { +			reg = ehci_readl(&ctrl->hcor->or_portsc[i]); +			reg |= EHCI_PS_SUSP; +			ehci_writel(&ctrl->hcor->or_portsc[i], reg); +		} + +		cmd &= ~CMD_RUN; +		ehci_writel(&ctrl->hcor->or_usbcmd, cmd); +		ret = handshake(&ctrl->hcor->or_usbsts, STS_HALT, STS_HALT, +			HCHALT_TIMEOUT); +	} + +	if (ret) +		puts("EHCI failed to shut down host controller.\n"); + +	return ret; +} +  static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)  {  	uint32_t delta, next; @@ -808,6 +844,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,  			}  			break;  		case USB_PORT_FEAT_TEST: +			ehci_shutdown(ctrl);  			reg &= ~(0xf << 16);  			reg |= ((le16_to_cpu(req->index) >> 8) & 0xf) << 16;  			ehci_writel(status_reg, reg); @@ -878,6 +915,7 @@ unknown:  int usb_lowlevel_stop(int index)  { +	ehci_shutdown(&ehcic[index]);  	return ehci_hcd_stop(index);  } diff --git a/drivers/usb/musb/musb_hcd.c b/drivers/usb/musb/musb_hcd.c index 41a8126b3..708fa124a 100644 --- a/drivers/usb/musb/musb_hcd.c +++ b/drivers/usb/musb/musb_hcd.c @@ -417,8 +417,12 @@ static int ctrlreq_out_data_phase(struct usb_device *dev, u32 len, void *buffer)  		/* Set TXPKTRDY bit */  		csr = readw(&musbr->txcsr); -		writew(csr | MUSB_CSR0_H_DIS_PING | MUSB_CSR0_TXPKTRDY, -					&musbr->txcsr); +			 +		csr |= MUSB_CSR0_TXPKTRDY; +#if !defined(CONFIG_SOC_DM365) +		csr |= MUSB_CSR0_H_DIS_PING; +#endif +		writew(csr, &musbr->txcsr);  		result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY);  		if (result < 0)  			break; @@ -439,8 +443,10 @@ static int ctrlreq_out_status_phase(struct usb_device *dev)  	/* Set the StatusPkt bit */  	csr = readw(&musbr->txcsr); -	csr |= (MUSB_CSR0_H_DIS_PING | MUSB_CSR0_TXPKTRDY | -			MUSB_CSR0_H_STATUSPKT); +	csr |= (MUSB_CSR0_TXPKTRDY | MUSB_CSR0_H_STATUSPKT); +#if !defined(CONFIG_SOC_DM365) +	csr |= MUSB_CSR0_H_DIS_PING; +#endif  	writew(csr, &musbr->txcsr);  	/* Wait until TXPKTRDY bit is cleared */ @@ -457,7 +463,10 @@ static int ctrlreq_in_status_phase(struct usb_device *dev)  	int result;  	/* Set the StatusPkt bit and ReqPkt bit */ -	csr = MUSB_CSR0_H_DIS_PING | MUSB_CSR0_H_REQPKT | MUSB_CSR0_H_STATUSPKT; +	csr = MUSB_CSR0_H_REQPKT | MUSB_CSR0_H_STATUSPKT; +#if !defined(CONFIG_SOC_DM365) +	csr |= MUSB_CSR0_H_DIS_PING; +#endif  	writew(csr, &musbr->txcsr);  	result = wait_until_ep0_ready(dev, MUSB_CSR0_H_REQPKT); |