diff options
Diffstat (limited to 'drivers')
67 files changed, 3962 insertions, 1934 deletions
| diff --git a/drivers/input/key_matrix.c b/drivers/input/key_matrix.c index 946a186a1..c900e45d1 100644 --- a/drivers/input/key_matrix.c +++ b/drivers/input/key_matrix.c @@ -154,54 +154,42 @@ static uchar *create_keymap(struct key_matrix *config, u32 *data, int len,  	return map;  } -int key_matrix_decode_fdt(struct key_matrix *config, const void *blob, -			  int node) +int key_matrix_decode_fdt(struct key_matrix *config, const void *blob, int node)  {  	const struct fdt_property *prop; -	const char prefix[] = "linux,"; -	int plen = sizeof(prefix) - 1; -	int offset; +	int proplen; +	uchar *plain_keycode; -	/* Check each property name for ones that we understand */ -	for (offset = fdt_first_property_offset(blob, node); -		      offset > 0; -		      offset = fdt_next_property_offset(blob, offset)) { -		const char *name; -		int len; +	prop = fdt_get_property(blob, node, "linux,keymap", &proplen); +	/* Basic keymap is required */ +	if (!prop) { +		debug("%s: cannot find keycode-plain map\n", __func__); +		return -1; +	} -		prop = fdt_get_property_by_offset(blob, offset, NULL); -		name = fdt_string(blob, fdt32_to_cpu(prop->nameoff)); -		len = strlen(name); +	plain_keycode = create_keymap(config, (u32 *)prop->data, +		proplen, KEY_FN, &config->fn_pos); +	config->plain_keycode = plain_keycode; +	/* Conversion error -> fail */ +	if (!config->plain_keycode) +		return -1; -		/* Name needs to match "1,<type>keymap" */ -		debug("%s: property '%s'\n", __func__, name); -		if (strncmp(name, prefix, plen) || -				len < plen + 6 || -				strcmp(name + len - 6, "keymap")) -			continue; +	prop = fdt_get_property(blob, node, "linux,fn-keymap", &proplen); +	/* fn keymap is optional */ +	if (!prop) +		goto done; -		len -= plen + 6; -		if (len == 0) { -			config->plain_keycode = create_keymap(config, -				(u32 *)prop->data, fdt32_to_cpu(prop->len), -				KEY_FN, &config->fn_pos); -		} else if (0 == strncmp(name + plen, "fn-", len)) { -			config->fn_keycode = create_keymap(config, -				(u32 *)prop->data, fdt32_to_cpu(prop->len), -				-1, NULL); -		} else { -			debug("%s: unrecognised property '%s'\n", __func__, -			      name); -		} -	} -	debug("%s: Decoded key maps %p, %p from fdt\n", __func__, -	      config->plain_keycode, config->fn_keycode); - -	if (!config->plain_keycode) { -		debug("%s: cannot find keycode-plain map\n", __func__); +	config->fn_keycode = create_keymap(config, (u32 *)prop->data, +		proplen, -1, NULL); +	/* Conversion error -> fail */ +	if (!config->fn_keycode) { +		free(plain_keycode);  		return -1;  	} +done: +	debug("%s: Decoded key maps %p, %p from fdt\n", __func__, +	      config->plain_keycode, config->fn_keycode);  	return 0;  } diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 861f4b9d6..973b19f33 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -178,7 +178,7 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data)  	int timeout;  	struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv;  	struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; -#ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO +#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO  	uint wml_value;  	wml_value = data->blocksize/4; @@ -310,6 +310,9 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)  	/* Figure out the transfer arguments */  	xfertyp = esdhc_xfertyp(cmd, data); +	/* Mask all irqs */ +	esdhc_write32(®s->irqsigen, 0); +  	/* Send the command */  	esdhc_write32(®s->cmdarg, cmd->cmdarg);  #if defined(CONFIG_FSL_USDHC) @@ -320,15 +323,11 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)  	esdhc_write32(®s->xfertyp, xfertyp);  #endif -	/* Mask all irqs */ -	esdhc_write32(®s->irqsigen, 0); -  	/* Wait for the command to complete */  	while (!(esdhc_read32(®s->irqstat) & (IRQSTAT_CC | IRQSTAT_CTOE)))  		;  	irqstat = esdhc_read32(®s->irqstat); -	esdhc_write32(®s->irqstat, irqstat);  	/* Reset CMD and DATA portions on error */  	if (irqstat & (CMD_ERR | IRQSTAT_CTOE)) { diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index a492bbb41..83d2df774 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -301,10 +301,12 @@ mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src)  		return 0;  	} -	if (blkcnt > 1) -		cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; -	else +	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; @@ -700,16 +702,49 @@ static int mmc_change_freq(struct mmc *mmc)  	return 0;  } +static int mmc_set_capacity(struct mmc *mmc, int part_num) +{ +	switch (part_num) { +	case 0: +		mmc->capacity = mmc->capacity_user; +		break; +	case 1: +	case 2: +		mmc->capacity = mmc->capacity_boot; +		break; +	case 3: +		mmc->capacity = mmc->capacity_rpmb; +		break; +	case 4: +	case 5: +	case 6: +	case 7: +		mmc->capacity = mmc->capacity_gp[part_num - 4]; +		break; +	default: +		return -1; +	} + +	mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len); + +	return 0; +} +  int mmc_switch_part(int dev_num, unsigned int part_num)  {  	struct mmc *mmc = find_mmc_device(dev_num); +	int ret;  	if (!mmc)  		return -1; -	return mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF, -			  (mmc->part_config & ~PART_ACCESS_MASK) -			  | (part_num & PART_ACCESS_MASK)); +	ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF, +			 (mmc->part_config & ~PART_ACCESS_MASK) +			 | (part_num & PART_ACCESS_MASK)); +	if (ret) +		return ret; + +	return mmc_set_capacity(mmc, part_num);  }  int mmc_getcd(struct mmc *mmc) @@ -917,7 +952,7 @@ static void mmc_set_bus_width(struct mmc *mmc, uint width)  static int mmc_startup(struct mmc *mmc)  { -	int err; +	int err, i;  	uint mult, freq;  	u64 cmult, csize, capacity;  	struct mmc_cmd cmd; @@ -1035,8 +1070,12 @@ static int mmc_startup(struct mmc *mmc)  		cmult = (mmc->csd[2] & 0x00038000) >> 15;  	} -	mmc->capacity = (csize + 1) << (cmult + 2); -	mmc->capacity *= mmc->read_bl_len; +	mmc->capacity_user = (csize + 1) << (cmult + 2); +	mmc->capacity_user *= mmc->read_bl_len; +	mmc->capacity_boot = 0; +	mmc->capacity_rpmb = 0; +	for (i = 0; i < 4; i++) +		mmc->capacity_gp[i] = 0;  	if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN)  		mmc->read_bl_len = MMC_MAX_BLOCK_LEN; @@ -1075,7 +1114,7 @@ static int mmc_startup(struct mmc *mmc)  					| ext_csd[EXT_CSD_SEC_CNT + 3] << 24;  			capacity *= MMC_MAX_BLOCK_LEN;  			if ((capacity >> 20) > 2 * 1024) -				mmc->capacity = capacity; +				mmc->capacity_user = capacity;  		}  		switch (ext_csd[EXT_CSD_REV]) { @@ -1117,8 +1156,25 @@ static int mmc_startup(struct mmc *mmc)  		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||  		    ext_csd[EXT_CSD_BOOT_MULT])  			mmc->part_config = ext_csd[EXT_CSD_PART_CONF]; + +		mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17; + +		mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17; + +		for (i = 0; i < 4; i++) { +			int idx = EXT_CSD_GP_SIZE_MULT + i * 3; +			mmc->capacity_gp[i] = (ext_csd[idx + 2] << 16) + +				(ext_csd[idx + 1] << 8) + ext_csd[idx]; +			mmc->capacity_gp[i] *= +				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; +			mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; +		}  	} +	err = mmc_set_capacity(mmc, mmc->part_num); +	if (err) +		return err; +  	if (IS_SD(mmc))  		err = sd_change_freq(mmc);  	else diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 1eaea04ad..c5631bff6 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -486,8 +486,10 @@ 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 (caps & SDHCI_CAN_DO_8BIT) -		mmc->host_caps |= MMC_MODE_8BIT; +	if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) { +		if (caps & SDHCI_CAN_DO_8BIT) +			mmc->host_caps |= MMC_MODE_8BIT; +	}  	if (host->host_caps)  		mmc->host_caps |= host->host_caps; diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 543c845ff..99f39fc75 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -25,7 +25,9 @@ include $(TOPDIR)/config.mk  LIB	:= $(obj)libmtd.o -COBJS-$(CONFIG_MTD_DEVICE) += mtdcore.o +ifneq (,$(findstring y,$(CONFIG_MTD_DEVICE)$(CONFIG_CMD_NAND)$(CONFIG_CMD_ONENAND))) +COBJS-y += mtdcore.o +endif  COBJS-$(CONFIG_MTD_PARTITIONS) += mtdpart.o  COBJS-$(CONFIG_MTD_CONCAT) += mtdconcat.o  COBJS-$(CONFIG_HAS_DATAFLASH) += at45.o diff --git a/drivers/mtd/cfi_mtd.c b/drivers/mtd/cfi_mtd.c index 8d74fa941..bbb71a19e 100644 --- a/drivers/mtd/cfi_mtd.c +++ b/drivers/mtd/cfi_mtd.c @@ -244,12 +244,12 @@ int cfi_mtd_init(void)  		mtd->size		= fi->size;  		mtd->writesize		= 1; -		mtd->erase		= cfi_mtd_erase; -		mtd->read		= cfi_mtd_read; -		mtd->write		= cfi_mtd_write; -		mtd->sync		= cfi_mtd_sync; -		mtd->lock		= cfi_mtd_lock; -		mtd->unlock		= cfi_mtd_unlock; +		mtd->_erase		= cfi_mtd_erase; +		mtd->_read		= cfi_mtd_read; +		mtd->_write		= cfi_mtd_write; +		mtd->_sync		= cfi_mtd_sync; +		mtd->_lock		= cfi_mtd_lock; +		mtd->_unlock		= cfi_mtd_unlock;  		mtd->priv		= fi;  		if (add_mtd_device(mtd)) diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index e6d938417..31e4289b1 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -70,14 +70,14 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len,  			/* Entire transaction goes into this subdev */  			size = len; -		err = subdev->read(subdev, from, size, &retsize, buf); +		err = mtd_read(subdev, from, size, &retsize, buf);  		/* Save information about bitflips! */  		if (unlikely(err)) { -			if (err == -EBADMSG) { +			if (mtd_is_eccerr(err)) {  				mtd->ecc_stats.failed++;  				ret = err; -			} else if (err == -EUCLEAN) { +			} else if (mtd_is_bitflip(err)) {  				mtd->ecc_stats.corrected++;  				/* Do not overwrite -EBADMSG !! */  				if (!ret) @@ -105,9 +105,6 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len,  	int err = -EINVAL;  	int i; -	if (!(mtd->flags & MTD_WRITEABLE)) -		return -EROFS; -  	*retlen = 0;  	for (i = 0; i < concat->num_subdev; i++) { @@ -124,11 +121,7 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len,  		else  			size = len; -		if (!(subdev->flags & MTD_WRITEABLE)) -			err = -EROFS; -		else -			err = subdev->write(subdev, to, size, &retsize, buf); - +		err = mtd_write(subdev, to, size, &retsize, buf);  		if (err)  			break; @@ -165,16 +158,16 @@ concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)  		if (from + devops.len > subdev->size)  			devops.len = subdev->size - from; -		err = subdev->read_oob(subdev, from, &devops); +		err = mtd_read_oob(subdev, from, &devops);  		ops->retlen += devops.retlen;  		ops->oobretlen += devops.oobretlen;  		/* Save information about bitflips! */  		if (unlikely(err)) { -			if (err == -EBADMSG) { +			if (mtd_is_eccerr(err)) {  				mtd->ecc_stats.failed++;  				ret = err; -			} else if (err == -EUCLEAN) { +			} else if (mtd_is_bitflip(err)) {  				mtd->ecc_stats.corrected++;  				/* Do not overwrite -EBADMSG !! */  				if (!ret) @@ -225,7 +218,7 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)  		if (to + devops.len > subdev->size)  			devops.len = subdev->size - to; -		err = subdev->write_oob(subdev, to, &devops); +		err = mtd_write_oob(subdev, to, &devops);  		ops->retlen += devops.retlen;  		if (err)  			return err; @@ -271,7 +264,7 @@ static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase)  	 * FIXME: Allow INTERRUPTIBLE. Which means  	 * not having the wait_queue head on the stack.  	 */ -	err = mtd->erase(mtd, erase); +	err = mtd_erase(mtd, erase);  	if (!err) {  		set_current_state(TASK_UNINTERRUPTIBLE);  		add_wait_queue(&waitq, &wait); @@ -294,15 +287,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)  	uint64_t length, offset = 0;  	struct erase_info *erase; -	if (!(mtd->flags & MTD_WRITEABLE)) -		return -EROFS; - -	if (instr->addr > concat->mtd.size) -		return -EINVAL; - -	if (instr->len + instr->addr > concat->mtd.size) -		return -EINVAL; -  	/*  	 * Check for proper erase block alignment of the to-be-erased area.  	 * It is easier to do this based on the super device's erase @@ -350,8 +334,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)  			return -EINVAL;  	} -	instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; -  	/* make a local copy of instr to avoid modifying the caller's struct */  	erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL); @@ -390,10 +372,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)  		else  			erase->len = length; -		if (!(subdev->flags & MTD_WRITEABLE)) { -			err = -EROFS; -			break; -		}  		length -= erase->len;  		if ((err = concat_dev_erase(subdev, erase))) {  			/* sanity check: should never happen since @@ -429,9 +407,6 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)  	struct mtd_concat *concat = CONCAT(mtd);  	int i, err = -EINVAL; -	if ((len + ofs) > mtd->size) -		return -EINVAL; -  	for (i = 0; i < concat->num_subdev; i++) {  		struct mtd_info *subdev = concat->subdev[i];  		uint64_t size; @@ -446,7 +421,7 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)  		else  			size = len; -		err = subdev->lock(subdev, ofs, size); +		err = mtd_lock(subdev, ofs, size);  		if (err)  			break; @@ -467,9 +442,6 @@ static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)  	struct mtd_concat *concat = CONCAT(mtd);  	int i, err = 0; -	if ((len + ofs) > mtd->size) -		return -EINVAL; -  	for (i = 0; i < concat->num_subdev; i++) {  		struct mtd_info *subdev = concat->subdev[i];  		uint64_t size; @@ -484,7 +456,7 @@ static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)  		else  			size = len; -		err = subdev->unlock(subdev, ofs, size); +		err = mtd_unlock(subdev, ofs, size);  		if (err)  			break; @@ -507,7 +479,7 @@ static void concat_sync(struct mtd_info *mtd)  	for (i = 0; i < concat->num_subdev; i++) {  		struct mtd_info *subdev = concat->subdev[i]; -		subdev->sync(subdev); +		mtd_sync(subdev);  	}  } @@ -516,12 +488,9 @@ static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs)  	struct mtd_concat *concat = CONCAT(mtd);  	int i, res = 0; -	if (!concat->subdev[0]->block_isbad) +	if (!mtd_can_have_bb(concat->subdev[0]))  		return res; -	if (ofs > mtd->size) -		return -EINVAL; -  	for (i = 0; i < concat->num_subdev; i++) {  		struct mtd_info *subdev = concat->subdev[i]; @@ -530,7 +499,7 @@ static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs)  			continue;  		} -		res = subdev->block_isbad(subdev, ofs); +		res = mtd_block_isbad(subdev, ofs);  		break;  	} @@ -542,12 +511,9 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)  	struct mtd_concat *concat = CONCAT(mtd);  	int i, err = -EINVAL; -	if (!concat->subdev[0]->block_markbad) +	if (!mtd_can_have_bb(concat->subdev[0]))  		return 0; -	if (ofs > mtd->size) -		return -EINVAL; -  	for (i = 0; i < concat->num_subdev; i++) {  		struct mtd_info *subdev = concat->subdev[i]; @@ -556,7 +522,7 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)  			continue;  		} -		err = subdev->block_markbad(subdev, ofs); +		err = mtd_block_markbad(subdev, ofs);  		if (!err)  			mtd->ecc_stats.badblocks++;  		break; @@ -609,14 +575,14 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],	/* subdevices to c  	concat->mtd.subpage_sft = subdev[0]->subpage_sft;  	concat->mtd.oobsize = subdev[0]->oobsize;  	concat->mtd.oobavail = subdev[0]->oobavail; -	if (subdev[0]->read_oob) -		concat->mtd.read_oob = concat_read_oob; -	if (subdev[0]->write_oob) -		concat->mtd.write_oob = concat_write_oob; -	if (subdev[0]->block_isbad) -		concat->mtd.block_isbad = concat_block_isbad; -	if (subdev[0]->block_markbad) -		concat->mtd.block_markbad = concat_block_markbad; +	if (subdev[0]->_read_oob) +		concat->mtd._read_oob = concat_read_oob; +	if (subdev[0]->_write_oob) +		concat->mtd._write_oob = concat_write_oob; +	if (subdev[0]->_block_isbad) +		concat->mtd._block_isbad = concat_block_isbad; +	if (subdev[0]->_block_markbad) +		concat->mtd._block_markbad = concat_block_markbad;  	concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks; @@ -653,8 +619,8 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],	/* subdevices to c  		if (concat->mtd.writesize   !=  subdev[i]->writesize ||  		    concat->mtd.subpage_sft != subdev[i]->subpage_sft ||  		    concat->mtd.oobsize    !=  subdev[i]->oobsize || -		    !concat->mtd.read_oob  != !subdev[i]->read_oob || -		    !concat->mtd.write_oob != !subdev[i]->write_oob) { +		    !concat->mtd._read_oob  != !subdev[i]->_read_oob || +		    !concat->mtd._write_oob != !subdev[i]->_write_oob) {  			kfree(concat);  			printk("Incompatible OOB or ECC data on \"%s\"\n",  			       subdev[i]->name); @@ -669,12 +635,12 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],	/* subdevices to c  	concat->num_subdev = num_devs;  	concat->mtd.name = name; -	concat->mtd.erase = concat_erase; -	concat->mtd.read = concat_read; -	concat->mtd.write = concat_write; -	concat->mtd.sync = concat_sync; -	concat->mtd.lock = concat_lock; -	concat->mtd.unlock = concat_unlock; +	concat->mtd._erase = concat_erase; +	concat->mtd._read = concat_read; +	concat->mtd._write = concat_write; +	concat->mtd._sync = concat_sync; +	concat->mtd._lock = concat_lock; +	concat->mtd._unlock = concat_unlock;  	/*  	 * Combine the erase block size info of the subdevices: diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 3a81adaf6..49c08145a 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -25,6 +25,11 @@ int add_mtd_device(struct mtd_info *mtd)  			mtd->index = i;  			mtd->usecount = 0; +			/* default value if not set by driver */ +			if (mtd->bitflip_threshold == 0) +				mtd->bitflip_threshold = mtd->ecc_strength; + +  			/* No need to get a refcount on the module containing  			   the notifier, since we hold the mtd_table_mutex */ @@ -186,3 +191,189 @@ void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset,  	}  }  #endif /* defined(CONFIG_CMD_MTDPARTS_SPREAD) */ + + /* + * Erase is an asynchronous operation.  Device drivers are supposed + * to call instr->callback() whenever the operation completes, even + * if it completes with a failure. + * Callers are supposed to pass a callback function and wait for it + * to be called before writing to the block. + */ +int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ +	if (instr->addr > mtd->size || instr->len > mtd->size - instr->addr) +		return -EINVAL; +	if (!(mtd->flags & MTD_WRITEABLE)) +		return -EROFS; +	instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; +	if (!instr->len) { +		instr->state = MTD_ERASE_DONE; +		mtd_erase_callback(instr); +		return 0; +	} +	return mtd->_erase(mtd, instr); +} + +int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, +	     u_char *buf) +{ +	if (from < 0 || from > mtd->size || len > mtd->size - from) +		return -EINVAL; +	if (!len) +		return 0; +	return mtd->_read(mtd, from, len, retlen, buf); +} + +int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, +	      const u_char *buf) +{ +	*retlen = 0; +	if (to < 0 || to > mtd->size || len > mtd->size - to) +		return -EINVAL; +	if (!mtd->_write || !(mtd->flags & MTD_WRITEABLE)) +		return -EROFS; +	if (!len) +		return 0; +	return mtd->_write(mtd, to, len, retlen, buf); +} + +/* + * In blackbox flight recorder like scenarios we want to make successful writes + * in interrupt context. panic_write() is only intended to be called when its + * known the kernel is about to panic and we need the write to succeed. Since + * the kernel is not going to be running for much longer, this function can + * break locks and delay to ensure the write succeeds (but not sleep). + */ +int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, +		    const u_char *buf) +{ +	*retlen = 0; +	if (!mtd->_panic_write) +		return -EOPNOTSUPP; +	if (to < 0 || to > mtd->size || len > mtd->size - to) +		return -EINVAL; +	if (!(mtd->flags & MTD_WRITEABLE)) +		return -EROFS; +	if (!len) +		return 0; +	return mtd->_panic_write(mtd, to, len, retlen, buf); +} + +int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) +{ +	ops->retlen = ops->oobretlen = 0; +	if (!mtd->_read_oob) +		return -EOPNOTSUPP; +	return mtd->_read_oob(mtd, from, ops); +} + +/* + * Method to access the protection register area, present in some flash + * devices. The user data is one time programmable but the factory data is read + * only. + */ +int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf, +			   size_t len) +{ +	if (!mtd->_get_fact_prot_info) +		return -EOPNOTSUPP; +	if (!len) +		return 0; +	return mtd->_get_fact_prot_info(mtd, buf, len); +} + +int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, +			   size_t *retlen, u_char *buf) +{ +	*retlen = 0; +	if (!mtd->_read_fact_prot_reg) +		return -EOPNOTSUPP; +	if (!len) +		return 0; +	return mtd->_read_fact_prot_reg(mtd, from, len, retlen, buf); +} + +int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf, +			   size_t len) +{ +	if (!mtd->_get_user_prot_info) +		return -EOPNOTSUPP; +	if (!len) +		return 0; +	return mtd->_get_user_prot_info(mtd, buf, len); +} + +int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, +			   size_t *retlen, u_char *buf) +{ +	*retlen = 0; +	if (!mtd->_read_user_prot_reg) +		return -EOPNOTSUPP; +	if (!len) +		return 0; +	return mtd->_read_user_prot_reg(mtd, from, len, retlen, buf); +} + +int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len, +			    size_t *retlen, u_char *buf) +{ +	*retlen = 0; +	if (!mtd->_write_user_prot_reg) +		return -EOPNOTSUPP; +	if (!len) +		return 0; +	return mtd->_write_user_prot_reg(mtd, to, len, retlen, buf); +} + +int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len) +{ +	if (!mtd->_lock_user_prot_reg) +		return -EOPNOTSUPP; +	if (!len) +		return 0; +	return mtd->_lock_user_prot_reg(mtd, from, len); +} + +/* Chip-supported device locking */ +int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ +	if (!mtd->_lock) +		return -EOPNOTSUPP; +	if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs) +		return -EINVAL; +	if (!len) +		return 0; +	return mtd->_lock(mtd, ofs, len); +} + +int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ +	if (!mtd->_unlock) +		return -EOPNOTSUPP; +	if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs) +		return -EINVAL; +	if (!len) +		return 0; +	return mtd->_unlock(mtd, ofs, len); +} + +int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ +	if (!mtd->_block_isbad) +		return 0; +	if (ofs < 0 || ofs > mtd->size) +		return -EINVAL; +	return mtd->_block_isbad(mtd, ofs); +} + +int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ +	if (!mtd->_block_markbad) +		return -EOPNOTSUPP; +	if (ofs < 0 || ofs > mtd->size) +		return -EINVAL; +	if (!(mtd->flags & MTD_WRITEABLE)) +		return -EROFS; +	return mtd->_block_markbad(mtd, ofs); +} + diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index cbfc6796c..9dfe7bbc9 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -52,17 +52,11 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,  	int res;  	stats = part->master->ecc_stats; - -	if (from >= mtd->size) -		len = 0; -	else if (from + len > mtd->size) -		len = mtd->size - from; -	res = part->master->read(part->master, from + part->offset, -				   len, retlen, buf); +	res = mtd_read(part->master, from + part->offset, len, retlen, buf);  	if (unlikely(res)) { -		if (res == -EUCLEAN) +		if (mtd_is_bitflip(res))  			mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected; -		if (res == -EBADMSG) +		if (mtd_is_eccerr(res))  			mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed;  	}  	return res; @@ -78,12 +72,12 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,  		return -EINVAL;  	if (ops->datbuf && from + ops->len > mtd->size)  		return -EINVAL; -	res = part->master->read_oob(part->master, from + part->offset, ops); +	res = mtd_read_oob(part->master, from + part->offset, ops);  	if (unlikely(res)) { -		if (res == -EUCLEAN) +		if (mtd_is_bitflip(res))  			mtd->ecc_stats.corrected++; -		if (res == -EBADMSG) +		if (mtd_is_eccerr(res))  			mtd->ecc_stats.failed++;  	}  	return res; @@ -93,58 +87,35 @@ static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,  		size_t len, size_t *retlen, u_char *buf)  {  	struct mtd_part *part = PART(mtd); -	return part->master->read_user_prot_reg(part->master, from, -					len, retlen, buf); +	return mtd_read_user_prot_reg(part->master, from, len, retlen, buf);  }  static int part_get_user_prot_info(struct mtd_info *mtd,  		struct otp_info *buf, size_t len)  {  	struct mtd_part *part = PART(mtd); -	return part->master->get_user_prot_info(part->master, buf, len); +	return mtd_get_user_prot_info(part->master, buf, len);  }  static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,  		size_t len, size_t *retlen, u_char *buf)  {  	struct mtd_part *part = PART(mtd); -	return part->master->read_fact_prot_reg(part->master, from, -					len, retlen, buf); +	return mtd_read_fact_prot_reg(part->master, from, len, retlen, buf);  }  static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,  		size_t len)  {  	struct mtd_part *part = PART(mtd); -	return part->master->get_fact_prot_info(part->master, buf, len); +	return mtd_get_fact_prot_info(part->master, buf, len);  }  static int part_write(struct mtd_info *mtd, loff_t to, size_t len,  		size_t *retlen, const u_char *buf)  {  	struct mtd_part *part = PART(mtd); -	if (!(mtd->flags & MTD_WRITEABLE)) -		return -EROFS; -	if (to >= mtd->size) -		len = 0; -	else if (to + len > mtd->size) -		len = mtd->size - to; -	return part->master->write(part->master, to + part->offset, -				    len, retlen, buf); -} - -static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len, -		size_t *retlen, const u_char *buf) -{ -	struct mtd_part *part = PART(mtd); -	if (!(mtd->flags & MTD_WRITEABLE)) -		return -EROFS; -	if (to >= mtd->size) -		len = 0; -	else if (to + len > mtd->size) -		len = mtd->size - to; -	return part->master->panic_write(part->master, to + part->offset, -				    len, retlen, buf); +	return mtd_write(part->master, to + part->offset, len, retlen, buf);  }  static int part_write_oob(struct mtd_info *mtd, loff_t to, @@ -152,41 +123,34 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to,  {  	struct mtd_part *part = PART(mtd); -	if (!(mtd->flags & MTD_WRITEABLE)) -		return -EROFS; -  	if (to >= mtd->size)  		return -EINVAL;  	if (ops->datbuf && to + ops->len > mtd->size)  		return -EINVAL; -	return part->master->write_oob(part->master, to + part->offset, ops); +	return mtd_write_oob(part->master, to + part->offset, ops);  }  static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,  		size_t len, size_t *retlen, u_char *buf)  {  	struct mtd_part *part = PART(mtd); -	return part->master->write_user_prot_reg(part->master, from, -					len, retlen, buf); +	return mtd_write_user_prot_reg(part->master, from, len, retlen, buf);  }  static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,  		size_t len)  {  	struct mtd_part *part = PART(mtd); -	return part->master->lock_user_prot_reg(part->master, from, len); +	return mtd_lock_user_prot_reg(part->master, from, len);  }  static int part_erase(struct mtd_info *mtd, struct erase_info *instr)  {  	struct mtd_part *part = PART(mtd);  	int ret; -	if (!(mtd->flags & MTD_WRITEABLE)) -		return -EROFS; -	if (instr->addr >= mtd->size) -		return -EINVAL; +  	instr->addr += part->offset; -	ret = part->master->erase(part->master, instr); +	ret = mtd_erase(part->master, instr);  	if (ret) {  		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)  			instr->fail_addr -= part->offset; @@ -197,7 +161,7 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr)  void mtd_erase_callback(struct erase_info *instr)  { -	if (instr->mtd->erase == part_erase) { +	if (instr->mtd->_erase == part_erase) {  		struct mtd_part *part = PART(instr->mtd);  		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) @@ -211,32 +175,26 @@ void mtd_erase_callback(struct erase_info *instr)  static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)  {  	struct mtd_part *part = PART(mtd); -	if ((len + ofs) > mtd->size) -		return -EINVAL; -	return part->master->lock(part->master, ofs + part->offset, len); +	return mtd_lock(part->master, ofs + part->offset, len);  }  static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)  {  	struct mtd_part *part = PART(mtd); -	if ((len + ofs) > mtd->size) -		return -EINVAL; -	return part->master->unlock(part->master, ofs + part->offset, len); +	return mtd_unlock(part->master, ofs + part->offset, len);  }  static void part_sync(struct mtd_info *mtd)  {  	struct mtd_part *part = PART(mtd); -	part->master->sync(part->master); +	mtd_sync(part->master);  }  static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)  {  	struct mtd_part *part = PART(mtd); -	if (ofs >= mtd->size) -		return -EINVAL;  	ofs += part->offset; -	return part->master->block_isbad(part->master, ofs); +	return mtd_block_isbad(part->master, ofs);  }  static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) @@ -244,12 +202,8 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)  	struct mtd_part *part = PART(mtd);  	int res; -	if (!(mtd->flags & MTD_WRITEABLE)) -		return -EROFS; -	if (ofs >= mtd->size) -		return -EINVAL;  	ofs += part->offset; -	res = part->master->block_markbad(part->master, ofs); +	res = mtd_block_markbad(part->master, ofs);  	if (!res)  		mtd->ecc_stats.badblocks++;  	return res; @@ -303,39 +257,36 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,  	slave->mtd.name = part->name;  	slave->mtd.owner = master->owner; -	slave->mtd.read = part_read; -	slave->mtd.write = part_write; - -	if (master->panic_write) -		slave->mtd.panic_write = part_panic_write; +	slave->mtd._read = part_read; +	slave->mtd._write = part_write; -	if (master->read_oob) -		slave->mtd.read_oob = part_read_oob; -	if (master->write_oob) -		slave->mtd.write_oob = part_write_oob; -	if (master->read_user_prot_reg) -		slave->mtd.read_user_prot_reg = part_read_user_prot_reg; -	if (master->read_fact_prot_reg) -		slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; -	if (master->write_user_prot_reg) -		slave->mtd.write_user_prot_reg = part_write_user_prot_reg; -	if (master->lock_user_prot_reg) -		slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; -	if (master->get_user_prot_info) -		slave->mtd.get_user_prot_info = part_get_user_prot_info; -	if (master->get_fact_prot_info) -		slave->mtd.get_fact_prot_info = part_get_fact_prot_info; -	if (master->sync) -		slave->mtd.sync = part_sync; -	if (master->lock) -		slave->mtd.lock = part_lock; -	if (master->unlock) -		slave->mtd.unlock = part_unlock; -	if (master->block_isbad) -		slave->mtd.block_isbad = part_block_isbad; -	if (master->block_markbad) -		slave->mtd.block_markbad = part_block_markbad; -	slave->mtd.erase = part_erase; +	if (master->_read_oob) +		slave->mtd._read_oob = part_read_oob; +	if (master->_write_oob) +		slave->mtd._write_oob = part_write_oob; +	if (master->_read_user_prot_reg) +		slave->mtd._read_user_prot_reg = part_read_user_prot_reg; +	if (master->_read_fact_prot_reg) +		slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg; +	if (master->_write_user_prot_reg) +		slave->mtd._write_user_prot_reg = part_write_user_prot_reg; +	if (master->_lock_user_prot_reg) +		slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg; +	if (master->_get_user_prot_info) +		slave->mtd._get_user_prot_info = part_get_user_prot_info; +	if (master->_get_fact_prot_info) +		slave->mtd._get_fact_prot_info = part_get_fact_prot_info; +	if (master->_sync) +		slave->mtd._sync = part_sync; +	if (master->_lock) +		slave->mtd._lock = part_lock; +	if (master->_unlock) +		slave->mtd._unlock = part_unlock; +	if (master->_block_isbad) +		slave->mtd._block_isbad = part_block_isbad; +	if (master->_block_markbad) +		slave->mtd._block_markbad = part_block_markbad; +	slave->mtd._erase = part_erase;  	slave->master = master;  	slave->offset = part->offset;  	slave->index = partno; @@ -416,12 +367,11 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,  	}  	slave->mtd.ecclayout = master->ecclayout; -	if (master->block_isbad) { +	if (master->_block_isbad) {  		uint64_t offs = 0;  		while (offs < slave->mtd.size) { -			if (master->block_isbad(master, -						offs + slave->offset)) +			if (mtd_block_isbad(master, offs + slave->offset))  				slave->mtd.ecc_stats.badblocks++;  			offs += slave->mtd.erasesize;  		} diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 882170491..bb81e8411 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -83,6 +83,7 @@ COBJS-$(CONFIG_NAND_DOCG4) += docg4.o  else  # minimal SPL drivers  COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o +COBJS-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o  COBJS-$(CONFIG_NAND_MXC) += mxc_nand_spl.o  endif # drivers diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 994dd9f09..3bfbaf8ac 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -489,7 +489,7 @@ normal_check:  }  static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, -	struct nand_chip *chip, uint8_t *buf, int page) +	struct nand_chip *chip, uint8_t *buf, int oob_required, int page)  {  	struct atmel_nand_host *host = chip->priv;  	int eccsize = chip->ecc.size; @@ -529,8 +529,9 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,  	return 0;  } -static void atmel_nand_pmecc_write_page(struct mtd_info *mtd, -		struct nand_chip *chip, const uint8_t *buf) +static int atmel_nand_pmecc_write_page(struct mtd_info *mtd, +		struct nand_chip *chip, const uint8_t *buf, +		int oob_required)  {  	struct atmel_nand_host *host = chip->priv;  	uint32_t *eccpos = chip->ecc.layout->eccpos; @@ -557,7 +558,7 @@ static void atmel_nand_pmecc_write_page(struct mtd_info *mtd,  	if (!timeout) {  		printk(KERN_ERR "atmel_nand : Timeout to read PMECC status, fail to write PMECC in oob\n"); -		return; +		goto out;  	}  	for (i = 0; i < host->pmecc_sector_number; i++) { @@ -570,6 +571,8 @@ static void atmel_nand_pmecc_write_page(struct mtd_info *mtd,  		}  	}  	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); +out: +	return 0;  }  static void atmel_pmecc_core_init(struct mtd_info *mtd) @@ -706,6 +709,7 @@ static int atmel_pmecc_nand_init_params(struct nand_chip *nand,  	nand->ecc.read_page = atmel_nand_pmecc_read_page;  	nand->ecc.write_page = atmel_nand_pmecc_write_page; +	nand->ecc.strength = cap;  	atmel_pmecc_core_init(mtd); @@ -775,9 +779,10 @@ static int atmel_nand_calculate(struct mtd_info *mtd,   * mtd:        mtd info structure   * chip:       nand chip info structure   * buf:        buffer to store read data + * oob_required:    caller expects OOB data read to chip->oob_poi   */ -static int atmel_nand_read_page(struct mtd_info *mtd, -		struct nand_chip *chip, uint8_t *buf, int page) +static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, +				uint8_t *buf, int oob_required, int page)  {  	int eccsize = chip->ecc.size;  	int eccbytes = chip->ecc.bytes; diff --git a/drivers/mtd/nand/bfin_nand.c b/drivers/mtd/nand/bfin_nand.c index c7ddbb21d..7e755e896 100644 --- a/drivers/mtd/nand/bfin_nand.c +++ b/drivers/mtd/nand/bfin_nand.c @@ -374,9 +374,11 @@ int board_nand_init(struct nand_chip *chip)  		if (!NAND_IS_512()) {  			chip->ecc.bytes = 3;  			chip->ecc.size = 256; +			chip->ecc.strength = 1;  		} else {  			chip->ecc.bytes = 6;  			chip->ecc.size = 512; +			chip->ecc.strength = 2;  		}  		chip->ecc.mode = NAND_ECC_HW;  		chip->ecc.calculate = bfin_nfc_calculate_ecc; diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index e8506ddd9..90f59857f 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -607,12 +607,13 @@ void davinci_nand_init(struct nand_chip *nand)  {  	nand->chip_delay  = 0;  #ifdef CONFIG_SYS_NAND_USE_FLASH_BBT -	nand->options	  |= NAND_USE_FLASH_BBT; +	nand->bbt_options	  |= NAND_BBT_USE_FLASH;  #endif  #ifdef CONFIG_SYS_NAND_HW_ECC  	nand->ecc.mode = NAND_ECC_HW;  	nand->ecc.size = 512;  	nand->ecc.bytes = 3; +	nand->ecc.strength = 1;  	nand->ecc.calculate = nand_davinci_calculate_ecc;  	nand->ecc.correct  = nand_davinci_correct_data;  	nand->ecc.hwctl  = nand_davinci_enable_hwecc; @@ -623,6 +624,7 @@ void davinci_nand_init(struct nand_chip *nand)  	nand->ecc.mode = NAND_ECC_HW_OOB_FIRST;  	nand->ecc.size = 512;  	nand->ecc.bytes = 10; +	nand->ecc.strength = 4;  	nand->ecc.calculate = nand_davinci_4bit_calculate_ecc;  	nand->ecc.correct = nand_davinci_4bit_correct_data;  	nand->ecc.hwctl = nand_davinci_4bit_enable_hwecc; diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index edf3a099b..4cd741ebb 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -134,7 +134,7 @@ static struct rs_control *rs_decoder;  /*   * The HW decoder in the DoC ASIC's provides us a error syndrome, - * which we must convert to a standard syndrom usable by the generic + * which we must convert to a standard syndrome usable by the generic   * Reed-Solomon library code.   *   * Fabrice Bellard figured this out in the old docecc code. I added @@ -154,7 +154,7 @@ static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc)  	ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2);  	parity = ecc[1]; -	/* Initialize the syndrom buffer */ +	/* Initialize the syndrome buffer */  	for (i = 0; i < NROOTS; i++)  		s[i] = ds[0];  	/* @@ -1033,7 +1033,7 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat,  		WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);  	else  		WriteDOC(DOC_ECC_DIS, docptr, ECCConf); -	if (no_ecc_failures && (ret == -EBADMSG)) { +	if (no_ecc_failures && mtd_is_eccerr(ret)) {  		printk(KERN_ERR "suppressing ECC failure\n");  		ret = 0;  	} @@ -1073,7 +1073,7 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const ch  	size_t retlen;  	for (offs = 0; offs < mtd->size; offs += mtd->erasesize) { -		ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf); +		ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf);  		if (retlen != mtd->writesize)  			continue;  		if (ret) { @@ -1098,7 +1098,7 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const ch  	/* Only one mediaheader was found.  We want buf to contain a  	   mediaheader on return, so we'll have to re-read the one we found. */  	offs = doc->mh0_page << this->page_shift; -	ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf); +	ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf);  	if (retlen != mtd->writesize) {  		/* Insanity.  Give up. */  		printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n"); @@ -1658,7 +1658,8 @@ static int __init doc_probe(unsigned long physadr)  	nand->ecc.mode		= NAND_ECC_HW_SYNDROME;  	nand->ecc.size		= 512;  	nand->ecc.bytes		= 6; -	nand->options		= NAND_USE_FLASH_BBT; +	nand->ecc.strength	= 2; +	nand->bbt_options	= NAND_BBT_USE_FLASH;  	doc->physadr		= physadr;  	doc->virtadr		= virtadr; diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 834a8a649..0fa776ae9 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -640,9 +640,8 @@ static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)  	return fsl_elbc_read_byte(mtd);  } -static int fsl_elbc_read_page(struct mtd_info *mtd, -			      struct nand_chip *chip, -			      uint8_t *buf, int page) +static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, +			      uint8_t *buf, int oob_required, int page)  {  	fsl_elbc_read_buf(mtd, buf, mtd->writesize);  	fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -656,12 +655,13 @@ static int fsl_elbc_read_page(struct mtd_info *mtd,  /* ECC will be calculated automatically, and errors will be detected in   * waitfunc.   */ -static void fsl_elbc_write_page(struct mtd_info *mtd, -				struct nand_chip *chip, -				const uint8_t *buf) +static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip, +				const uint8_t *buf, int oob_required)  {  	fsl_elbc_write_buf(mtd, buf, mtd->writesize);  	fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + +	return 0;  }  static struct fsl_elbc_ctrl *elbc_ctrl; @@ -747,8 +747,8 @@ static int fsl_elbc_chip_init(int devnum, u8 *addr)  	nand->bbt_md = &bbt_mirror_descr;    	/* set up nand options */ -	nand->options = NAND_NO_READRDY | NAND_NO_AUTOINCR | -			NAND_USE_FLASH_BBT | NAND_NO_SUBPAGE_WRITE; +	nand->options = NAND_NO_SUBPAGE_WRITE; +	nand->bbt_options = NAND_BBT_USE_FLASH;  	nand->controller = &elbc_ctrl->controller;  	nand->priv = priv; @@ -756,20 +756,8 @@ static int fsl_elbc_chip_init(int devnum, u8 *addr)  	nand->ecc.read_page = fsl_elbc_read_page;  	nand->ecc.write_page = fsl_elbc_write_page; -#ifdef CONFIG_FSL_ELBC_FMR -	priv->fmr = CONFIG_FSL_ELBC_FMR; -#else  	priv->fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT); -	/* -	 * Hardware expects small page has ECCM0, large page has ECCM1 -	 * when booting from NAND.  Board config can override if not -	 * booting from NAND. -	 */ -	if (or & OR_FCM_PGS) -		priv->fmr |= FMR_ECCM; -#endif -  	/* If CS Base Register selects full hardware ECC then use it */  	if ((br & BR_DECC) == BR_DECC_CHK_GEN) {  		nand->ecc.mode = NAND_ECC_HW; @@ -781,16 +769,32 @@ static int fsl_elbc_chip_init(int devnum, u8 *addr)  		nand->ecc.size = 512;  		nand->ecc.bytes = 3;  		nand->ecc.steps = 1; +		nand->ecc.strength = 1;  	} else {  		/* otherwise fall back to default software ECC */  		nand->ecc.mode = NAND_ECC_SOFT;  	} +	ret = nand_scan_ident(mtd, 1, NULL); +	if (ret) +		return ret; +  	/* Large-page-specific setup */ -	if (or & OR_FCM_PGS) { +	if (mtd->writesize == 2048) { +		setbits_be32(&elbc_ctrl->regs->bank[priv->bank].or, +			     OR_FCM_PGS); +		in_be32(&elbc_ctrl->regs->bank[priv->bank].or); +  		priv->page_size = 1;  		nand->badblock_pattern = &largepage_memorybased; +		/* +		 * Hardware expects small page has ECCM0, large page has +		 * ECCM1 when booting from NAND, and we follow that even +		 * when not booting from NAND. +		 */ +		priv->fmr |= FMR_ECCM; +  		/* adjust ecc setup if needed */  		if ((br & BR_DECC) == BR_DECC_CHK_GEN) {  			nand->ecc.steps = 4; @@ -798,12 +802,14 @@ static int fsl_elbc_chip_init(int devnum, u8 *addr)  					   &fsl_elbc_oob_lp_eccm1 :  					   &fsl_elbc_oob_lp_eccm0;  		} +	} else if (mtd->writesize == 512) { +		clrbits_be32(&elbc_ctrl->regs->bank[priv->bank].or, +			     OR_FCM_PGS); +		in_be32(&elbc_ctrl->regs->bank[priv->bank].or); +	} else { +		return -ENODEV;  	} -	ret = nand_scan_ident(mtd, 1, NULL); -	if (ret) -		return ret; -  	ret = nand_scan_tail(mtd);  	if (ret)  		return ret; diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index b13d8a930..439822c5a 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -21,6 +21,7 @@  #include <common.h>  #include <malloc.h> +#include <nand.h>  #include <linux/mtd/mtd.h>  #include <linux/mtd/nand.h> @@ -41,7 +42,6 @@ struct fsl_ifc_ctrl;  /* mtd information per set */  struct fsl_ifc_mtd { -	struct mtd_info mtd;  	struct nand_chip chip;  	struct fsl_ifc_ctrl *ctrl; @@ -686,9 +686,8 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip)  	return nand_fsr;  } -static int fsl_ifc_read_page(struct mtd_info *mtd, -			      struct nand_chip *chip, -			      uint8_t *buf, int page) +static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip, +			     uint8_t *buf, int oob_required, int page)  {  	struct fsl_ifc_mtd *priv = chip->priv;  	struct fsl_ifc_ctrl *ctrl = priv->ctrl; @@ -705,12 +704,13 @@ static int fsl_ifc_read_page(struct mtd_info *mtd,  /* ECC will be calculated automatically, and errors will be detected in   * waitfunc.   */ -static void fsl_ifc_write_page(struct mtd_info *mtd, -				struct nand_chip *chip, -				const uint8_t *buf) +static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip, +			       const uint8_t *buf, int oob_required)  {  	fsl_ifc_write_buf(mtd, buf, mtd->writesize);  	fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + +	return 0;  }  static void fsl_ifc_ctrl_init(void) @@ -794,11 +794,14 @@ static void fsl_ifc_sram_init(void)  	out_be32(&ifc_ctrl->regs->csor_cs[cs].csor_ext, csor_ext);  } -int board_nand_init(struct nand_chip *nand) +static int fsl_ifc_chip_init(int devnum, u8 *addr)  { +	struct mtd_info *mtd = &nand_info[devnum]; +	struct nand_chip *nand;  	struct fsl_ifc_mtd *priv;  	struct nand_ecclayout *layout;  	uint32_t cspr = 0, csor = 0, ver = 0; +	int ret;  	if (!ifc_ctrl) {  		fsl_ifc_ctrl_init(); @@ -811,18 +814,18 @@ int board_nand_init(struct nand_chip *nand)  		return -ENOMEM;  	priv->ctrl = ifc_ctrl; -	priv->vbase = nand->IO_ADDR_R; +	priv->vbase = addr;  	/* Find which chip select it is connected to.  	 */  	for (priv->bank = 0; priv->bank < MAX_BANKS; priv->bank++) { -		phys_addr_t base_addr = virt_to_phys(nand->IO_ADDR_R); +		phys_addr_t phys_addr = virt_to_phys(addr);  		cspr = in_be32(&ifc_ctrl->regs->cspr_cs[priv->bank].cspr);  		csor = in_be32(&ifc_ctrl->regs->csor_cs[priv->bank].csor);  		if ((cspr & CSPR_V) && (cspr & CSPR_MSEL) == CSPR_MSEL_NAND && -		    (cspr & CSPR_BA) == CSPR_PHYS_ADDR(base_addr)) { +		    (cspr & CSPR_BA) == CSPR_PHYS_ADDR(phys_addr)) {  			ifc_ctrl->cs_nand = priv->bank << IFC_NAND_CSEL_SHIFT;  			break;  		} @@ -835,6 +838,9 @@ int board_nand_init(struct nand_chip *nand)  		return -ENODEV;  	} +	nand = &priv->chip; +	mtd->priv = nand; +  	ifc_ctrl->chips[priv->bank] = priv;  	/* fill in nand_chip structure */ @@ -852,8 +858,8 @@ int board_nand_init(struct nand_chip *nand)  	nand->bbt_md = &bbt_mirror_descr;  	/* set up nand options */ -	nand->options = NAND_NO_READRDY | NAND_NO_AUTOINCR | -			NAND_USE_FLASH_BBT | NAND_NO_SUBPAGE_WRITE; +	nand->options = NAND_NO_SUBPAGE_WRITE; +	nand->bbt_options = NAND_BBT_USE_FLASH;  	if (cspr & CSPR_PORT_SIZE_16) {  		nand->read_byte = fsl_ifc_read_byte16; @@ -884,11 +890,13 @@ int board_nand_init(struct nand_chip *nand)  			bbt_mirror_descr.offs = 0;  		} +		nand->ecc.strength = 4;  		priv->bufnum_mask = 15;  		break;  	case CSOR_NAND_PGS_2K:  		layout = &oob_2048_ecc4; +		nand->ecc.strength = 4;  		priv->bufnum_mask = 3;  		break; @@ -896,8 +904,10 @@ int board_nand_init(struct nand_chip *nand)  		if ((csor & CSOR_NAND_ECC_MODE_MASK) ==  		    CSOR_NAND_ECC_MODE_4) {  			layout = &oob_4096_ecc4; +			nand->ecc.strength = 4;  		} else {  			layout = &oob_4096_ecc8; +			nand->ecc.strength = 8;  			nand->ecc.bytes = 16;  		} @@ -921,5 +931,31 @@ int board_nand_init(struct nand_chip *nand)  	if (ver == FSL_IFC_V1_1_0)  		fsl_ifc_sram_init(); +	ret = nand_scan_ident(mtd, 1, NULL); +	if (ret) +		return ret; + +	ret = nand_scan_tail(mtd); +	if (ret) +		return ret; + +	ret = nand_register(devnum); +	if (ret) +		return ret;  	return 0;  } + +#ifndef CONFIG_SYS_NAND_BASE_LIST +#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } +#endif + +static unsigned long base_address[CONFIG_SYS_MAX_NAND_DEVICE] = +	CONFIG_SYS_NAND_BASE_LIST; + +void board_nand_init(void) +{ +	int i; + +	for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) +		fsl_ifc_chip_init(i, (u8 *)base_address[i]); +} diff --git a/drivers/mtd/nand/fsl_ifc_spl.c b/drivers/mtd/nand/fsl_ifc_spl.c new file mode 100644 index 000000000..8537c4c6f --- /dev/null +++ b/drivers/mtd/nand/fsl_ifc_spl.c @@ -0,0 +1,258 @@ +/* + * NAND boot for Freescale Integrated Flash Controller, NAND FCM + * + * Copyright 2011 Freescale Semiconductor, Inc. + * Author: Dipen Dudhat <dipen.dudhat@freescale.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/fsl_ifc.h> +#include <linux/mtd/nand.h> + +static inline int is_blank(uchar *addr, int page_size) +{ +	int i; + +	for (i = 0; i < page_size; i++) { +		if (__raw_readb(&addr[i]) != 0xff) +			return 0; +	} + +	/* +	 * For the SPL, don't worry about uncorrectable errors +	 * where the main area is all FFs but shouldn't be. +	 */ +	return 1; +} + +/* returns nonzero if entire page is blank */ +static inline int check_read_ecc(uchar *buf, u32 *eccstat, +				 unsigned int bufnum, int page_size) +{ +	u32 reg = eccstat[bufnum / 4]; +	int errors = (reg >> ((3 - bufnum % 4) * 8)) & 0xf; + +	if (errors == 0xf) { /* uncorrectable */ +		/* Blank pages fail hw ECC checks */ +		if (is_blank(buf, page_size)) +			return 1; + +		puts("ecc error\n"); +		for (;;) +			; +	} + +	return 0; +} + +static inline void nand_wait(uchar *buf, int bufnum, int page_size) +{ +	struct fsl_ifc *ifc = IFC_BASE_ADDR; +	u32 status; +	u32 eccstat[4]; +	int bufperpage = page_size / 512; +	int bufnum_end, i; + +	bufnum *= bufperpage; +	bufnum_end = bufnum + bufperpage - 1; + +	do { +		status = in_be32(&ifc->ifc_nand.nand_evter_stat); +	} while (!(status & IFC_NAND_EVTER_STAT_OPC)); + +	if (status & IFC_NAND_EVTER_STAT_FTOER) { +		puts("flash time out error\n"); +		for (;;) +			; +	} + +	for (i = bufnum / 4; i <= bufnum_end / 4; i++) +		eccstat[i] = in_be32(&ifc->ifc_nand.nand_eccstat[i]); + +	for (i = bufnum; i <= bufnum_end; i++) { +		if (check_read_ecc(buf, eccstat, i, page_size)) +			break; +	} + +	out_be32(&ifc->ifc_nand.nand_evter_stat, status); +} + +static inline int bad_block(uchar *marker, int port_size) +{ +	if (port_size == 8) +		return __raw_readb(marker) != 0xff; +	else +		return __raw_readw((u16 *)marker) != 0xffff; +} + +static void nand_load(unsigned int offs, int uboot_size, uchar *dst) +{ +	struct fsl_ifc *ifc = IFC_BASE_ADDR; +	uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE; +	int page_size; +	int port_size; +	int pages_per_blk; +	int blk_size; +	int bad_marker = 0; +	int bufnum_mask, bufnum; + +	int csor, cspr; +	int pos = 0; +	int j = 0; + +	int sram_addr; +	int pg_no; + +	/* Get NAND Flash configuration */ +	csor = CONFIG_SYS_NAND_CSOR; +	cspr = CONFIG_SYS_NAND_CSPR; + +	port_size = (cspr & CSPR_PORT_SIZE_16) ? 16 : 8; + +	if (csor & CSOR_NAND_PGS_4K) { +		page_size = 4096; +		bufnum_mask = 0x1; +	} else if (csor & CSOR_NAND_PGS_2K) { +		page_size = 2048; +		bufnum_mask = 0x3; +	} else { +		page_size = 512; +		bufnum_mask = 0xf; + +		if (port_size == 8) +			bad_marker = 5; +	} + +	pages_per_blk = +		32 << ((csor & CSOR_NAND_PB_MASK) >> CSOR_NAND_PB_SHIFT); + +	blk_size = pages_per_blk * page_size; + +	/* Open Full SRAM mapping for spare are access */ +	out_be32(&ifc->ifc_nand.ncfgr, 0x0); + +	/* Clear Boot events */ +	out_be32(&ifc->ifc_nand.nand_evter_stat, 0xffffffff); + +	/* Program FIR/FCR for Large/Small page */ +	if (page_size > 512) { +		out_be32(&ifc->ifc_nand.nand_fir0, +			 (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | +			 (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | +			 (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | +			 (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | +			 (IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP4_SHIFT)); +		out_be32(&ifc->ifc_nand.nand_fir1, 0x0); + +		out_be32(&ifc->ifc_nand.nand_fcr0, +			 (NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | +			 (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT)); +	} else { +		out_be32(&ifc->ifc_nand.nand_fir0, +			 (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | +			 (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | +			 (IFC_FIR_OP_RA0  << IFC_NAND_FIR0_OP2_SHIFT) | +			 (IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP3_SHIFT)); +		out_be32(&ifc->ifc_nand.nand_fir1, 0x0); + +		out_be32(&ifc->ifc_nand.nand_fcr0, +			 NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT); +	} + +	/* Program FBCR = 0 for full page read */ +	out_be32(&ifc->ifc_nand.nand_fbcr, 0); + +	/* Read and copy u-boot on SDRAM from NAND device, In parallel +	 * check for Bad block if found skip it and read continue to +	 * next Block +	 */ +	while (pos < uboot_size) { +		int i = 0; +		do { +			pg_no = offs / page_size; +			bufnum = pg_no & bufnum_mask; +			sram_addr = bufnum * page_size * 2; + +			out_be32(&ifc->ifc_nand.row0, pg_no); +			out_be32(&ifc->ifc_nand.col0, 0); +			/* start read */ +			out_be32(&ifc->ifc_nand.nandseq_strt, +				 IFC_NAND_SEQ_STRT_FIR_STRT); + +			/* wait for read to complete */ +			nand_wait(&buf[sram_addr], bufnum, page_size); + +			/* +			 * If either of the first two pages are marked bad, +			 * continue to the next block. +			 */ +			if (i++ < 2 && +			    bad_block(&buf[sram_addr + page_size + bad_marker], +				      port_size)) { +				puts("skipping\n"); +				offs = (offs + blk_size) & ~(blk_size - 1); +				pos &= ~(blk_size - 1); +				break; +			} + +			for (j = 0; j < page_size; j++) +				dst[pos + j] = __raw_readb(&buf[sram_addr + j]); + +			pos += page_size; +			offs += page_size; +		} while ((offs & (blk_size - 1)) && (pos < uboot_size)); +	} +} + +/* + * Main entrypoint for NAND Boot. It's necessary that SDRAM is already + * configured and available since this code loads the main U-boot image + * from NAND into SDRAM and starts from there. + */ +void nand_boot(void) +{ +	__attribute__((noreturn)) void (*uboot)(void); +	/* +	 * Load U-Boot image from NAND into RAM +	 */ +	nand_load(CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_SIZE, +		  (uchar *)CONFIG_SYS_NAND_U_BOOT_DST); + +#ifdef CONFIG_NAND_ENV_DST +	nand_load(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, +		  (uchar *)CONFIG_NAND_ENV_DST); + +#ifdef CONFIG_ENV_OFFSET_REDUND +	nand_load(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, +		  (uchar *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE); +#endif +#endif +	/* +	 * Jump to U-Boot image +	 */ +#ifdef CONFIG_SPL_FLUSH_IMAGE +	/* +	 * Clean d-cache and invalidate i-cache, to +	 * make sure that no stale data is executed. +	 */ +	flush_cache(CONFIG_SYS_NAND_U_BOOT_DST, CONFIG_SYS_NAND_U_BOOT_SIZE); +#endif +	uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; +	uboot(); +} diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 7a61d88cc..fab2aebc3 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -341,6 +341,7 @@ void fsmc_enable_hwecc(struct mtd_info *mtd, int mode)   * @mtd:	mtd info structure   * @chip:	nand chip info structure   * @buf:	buffer to store read data + * @oob_required:	caller expects OOB data read to chip->oob_poi   * @page:	page number to read   *   * This routine is needed for fsmc verison 8 as reading from NAND chip has to be @@ -350,7 +351,7 @@ void fsmc_enable_hwecc(struct mtd_info *mtd, int mode)   * max of 8 bits)   */  static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, -				 uint8_t *buf, int page) +				 uint8_t *buf, int oob_required, int page)  {  	struct fsmc_eccplace *fsmc_eccpl;  	int i, j, s, stat, eccsize = chip->ecc.size; @@ -452,6 +453,7 @@ int fsmc_nand_init(struct nand_chip *nand)  	switch (fsmc_version) {  	case FSMC_VER8:  		nand->ecc.bytes = 13; +		nand->ecc.strength = 8;  		nand->ecc.correct = fsmc_bch8_correct_data;  		nand->ecc.read_page = fsmc_read_page_hwecc;  		if (mtd->writesize == 512) @@ -466,6 +468,7 @@ int fsmc_nand_init(struct nand_chip *nand)  		break;  	default:  		nand->ecc.bytes = 3; +		nand->ecc.strength = 1;  		nand->ecc.layout = &fsmc_ecc1_layout;  		nand->ecc.correct = nand_correct_data;  		break; diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index 3ec34f3c9..a691fbc40 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -253,6 +253,7 @@ int board_nand_init(struct nand_chip *nand)  	nand->ecc.mode		= NAND_ECC_HW_OOB_FIRST;  	nand->ecc.size		= CONFIG_SYS_NAND_ECCSIZE;  	nand->ecc.bytes		= CONFIG_SYS_NAND_ECCBYTES; +	nand->ecc.strength	= 4;  	nand->ecc.layout	= &qi_lb60_ecclayout_2gb;  	nand->chip_delay	= 50;  	nand->options		= NAND_USE_FLASH_BBT; diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index e6b7a7066..e53f341dc 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -621,7 +621,7 @@ int board_nand_init(struct nand_chip *chip)  	chip->write_buf = mpc5121_nfc_write_buf;  	chip->verify_buf = mpc5121_nfc_verify_buf;  	chip->select_chip = mpc5121_nfc_select_chip; -	chip->options = NAND_NO_AUTOINCR | NAND_USE_FLASH_BBT; +	chip->bbt_options = NAND_BBT_USE_FLASH;  	chip->ecc.mode = NAND_ECC_SOFT;  	/* Reset NAND Flash controller */ diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index eeba52194..ac435f205 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -396,7 +396,7 @@ static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode)  #if defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2)  static int mxc_nand_read_oob_syndrome(struct mtd_info *mtd,  				      struct nand_chip *chip, -				      int page, int sndcmd) +				      int page)  {  	struct mxc_nand_host *host = chip->priv;  	uint8_t *buf = chip->oob_poi; @@ -450,6 +450,7 @@ static int mxc_nand_read_oob_syndrome(struct mtd_info *mtd,  static int mxc_nand_read_page_raw_syndrome(struct mtd_info *mtd,  					   struct nand_chip *chip,  					   uint8_t *buf, +					   int oob_required,  					   int page)  {  	struct mxc_nand_host *host = chip->priv; @@ -494,6 +495,7 @@ static int mxc_nand_read_page_raw_syndrome(struct mtd_info *mtd,  static int mxc_nand_read_page_syndrome(struct mtd_info *mtd,  				       struct nand_chip *chip,  				       uint8_t *buf, +				       int oob_required,  				       int page)  {  	struct mxc_nand_host *host = chip->priv; @@ -583,9 +585,10 @@ static int mxc_nand_write_oob_syndrome(struct mtd_info *mtd,  	return status & NAND_STATUS_FAIL ? -EIO : 0;  } -static void mxc_nand_write_page_raw_syndrome(struct mtd_info *mtd, +static int mxc_nand_write_page_raw_syndrome(struct mtd_info *mtd,  					     struct nand_chip *chip, -					     const uint8_t *buf) +					     const uint8_t *buf, +					     int oob_required)  {  	struct mxc_nand_host *host = chip->priv;  	int eccsize = chip->ecc.size; @@ -619,11 +622,13 @@ static void mxc_nand_write_page_raw_syndrome(struct mtd_info *mtd,  	size = mtd->oobsize - (oob - chip->oob_poi);  	if (size)  		chip->write_buf(mtd, oob, size); +	return 0;  } -static void mxc_nand_write_page_syndrome(struct mtd_info *mtd, +static int mxc_nand_write_page_syndrome(struct mtd_info *mtd,  					 struct nand_chip *chip, -					 const uint8_t *buf) +					 const uint8_t *buf, +					 int oob_required)  {  	struct mxc_nand_host *host = chip->priv;  	int i, n, eccsize = chip->ecc.size; @@ -662,6 +667,7 @@ static void mxc_nand_write_page_syndrome(struct mtd_info *mtd,  	i = mtd->oobsize - (oob - chip->oob_poi);  	if (i)  		chip->write_buf(mtd, oob, i); +	return 0;  }  static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, @@ -1188,7 +1194,7 @@ int board_nand_init(struct nand_chip *this)  #endif  #ifdef CONFIG_SYS_NAND_USE_FLASH_BBT -	this->options |= NAND_USE_FLASH_BBT; +	this->bbt_options |= NAND_BBT_USE_FLASH;  	this->bbt_td = &bbt_main_descr;  	this->bbt_md = &bbt_mirror_descr;  #endif @@ -1236,6 +1242,13 @@ int board_nand_init(struct nand_chip *this)  		this->ecc.mode = NAND_ECC_HW;  	} +	if (this->ecc.mode == NAND_ECC_HW) { +		if (is_mxc_nfc_1()) +			this->ecc.strength = 1; +		else +			this->ecc.strength = 4; +	} +  	host->pagesize_2k = 0;  	this->ecc.size = 512; diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index 398e4ddc1..866cabd27 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -553,7 +553,8 @@ static uint8_t mxs_nand_read_byte(struct mtd_info *mtd)   * Read a page from NAND.   */  static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, -					uint8_t *buf, int page) +					uint8_t *buf, int oob_required, +					int page)  {  	struct mxs_nand_info *nand_info = nand->priv;  	struct mxs_dma_desc *d; @@ -698,8 +699,9 @@ rtn:  /*   * Write a page to NAND.   */ -static void mxs_nand_ecc_write_page(struct mtd_info *mtd, -				struct nand_chip *nand, const uint8_t *buf) +static int mxs_nand_ecc_write_page(struct mtd_info *mtd, +				struct nand_chip *nand, const uint8_t *buf, +				int oob_required)  {  	struct mxs_nand_info *nand_info = nand->priv;  	struct mxs_dma_desc *d; @@ -755,6 +757,7 @@ static void mxs_nand_ecc_write_page(struct mtd_info *mtd,  rtn:  	mxs_nand_return_dma_descs(nand_info); +	return 0;  }  /* @@ -770,7 +773,7 @@ static int mxs_nand_hook_read_oob(struct mtd_info *mtd, loff_t from,  	struct mxs_nand_info *nand_info = chip->priv;  	int ret; -	if (ops->mode == MTD_OOB_RAW) +	if (ops->mode == MTD_OPS_RAW)  		nand_info->raw_oob_mode = 1;  	else  		nand_info->raw_oob_mode = 0; @@ -795,7 +798,7 @@ static int mxs_nand_hook_write_oob(struct mtd_info *mtd, loff_t to,  	struct mxs_nand_info *nand_info = chip->priv;  	int ret; -	if (ops->mode == MTD_OOB_RAW) +	if (ops->mode == MTD_OPS_RAW)  		nand_info->raw_oob_mode = 1;  	else  		nand_info->raw_oob_mode = 0; @@ -873,7 +876,7 @@ static int mxs_nand_hook_block_markbad(struct mtd_info *mtd, loff_t ofs)   * what to do.   */  static int mxs_nand_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *nand, -				int page, int cmd) +				int page)  {  	struct mxs_nand_info *nand_info = nand->priv; @@ -1006,19 +1009,19 @@ static int mxs_nand_scan_bbt(struct mtd_info *mtd)  	writel(BCH_CTRL_COMPLETE_IRQ_EN, &bch_regs->hw_bch_ctrl_set);  	/* Hook some operations at the MTD level. */ -	if (mtd->read_oob != mxs_nand_hook_read_oob) { -		nand_info->hooked_read_oob = mtd->read_oob; -		mtd->read_oob = mxs_nand_hook_read_oob; +	if (mtd->_read_oob != mxs_nand_hook_read_oob) { +		nand_info->hooked_read_oob = mtd->_read_oob; +		mtd->_read_oob = mxs_nand_hook_read_oob;  	} -	if (mtd->write_oob != mxs_nand_hook_write_oob) { -		nand_info->hooked_write_oob = mtd->write_oob; -		mtd->write_oob = mxs_nand_hook_write_oob; +	if (mtd->_write_oob != mxs_nand_hook_write_oob) { +		nand_info->hooked_write_oob = mtd->_write_oob; +		mtd->_write_oob = mxs_nand_hook_write_oob;  	} -	if (mtd->block_markbad != mxs_nand_hook_block_markbad) { -		nand_info->hooked_block_markbad = mtd->block_markbad; -		mtd->block_markbad = mxs_nand_hook_block_markbad; +	if (mtd->_block_markbad != mxs_nand_hook_block_markbad) { +		nand_info->hooked_block_markbad = mtd->_block_markbad; +		mtd->_block_markbad = mxs_nand_hook_block_markbad;  	}  	/* We use the reference implementation for bad block management. */ @@ -1172,6 +1175,7 @@ int board_nand_init(struct nand_chip *nand)  	nand->ecc.mode		= NAND_ECC_HW;  	nand->ecc.bytes		= 9;  	nand->ecc.size		= 512; +	nand->ecc.strength	= 8;  	return 0; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index a2d06be99..9e05cef41 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -21,7 +21,7 @@   *  TODO:   *	Enable cached programming for 2k page size chips   *	Check, if mtd->ecctype should be set to MTD_ECC_HW - *	if we have HW ecc support. + *	if we have HW ECC support.   *	The AG-AND chips have nice features for speed improvement,   *	which are not supported yet. Read / program 4 pages in one go.   *	BBT table is not serialized, has to be fixed @@ -134,21 +134,14 @@ static int check_offs_len(struct mtd_info *mtd,  		ret = -EINVAL;  	} -	/* Do not allow past end of device */ -	if (ofs + len > mtd->size) { -		MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Past end of device\n", -					__func__); -		ret = -EINVAL; -	} -  	return ret;  }  /**   * nand_release_device - [GENERIC] release chip - * @mtd:	MTD device structure + * @mtd: MTD device structure   * - * Deselect, release chip lock and wake up anyone waiting on the device + * Deselect, release chip lock and wake up anyone waiting on the device.   */  static void nand_release_device(struct mtd_info *mtd)  { @@ -160,9 +153,9 @@ static void nand_release_device(struct mtd_info *mtd)  /**   * nand_read_byte - [DEFAULT] read one byte from the chip - * @mtd:	MTD device structure + * @mtd: MTD device structure   * - * Default read function for 8bit buswith + * Default read function for 8bit buswidth.   */  uint8_t nand_read_byte(struct mtd_info *mtd)  { @@ -172,10 +165,11 @@ uint8_t nand_read_byte(struct mtd_info *mtd)  /**   * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip - * @mtd:	MTD device structure + * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip + * @mtd: MTD device structure + * + * Default read function for 16bit buswidth with endianness conversion.   * - * Default read function for 16bit buswith with - * endianess conversion   */  static uint8_t nand_read_byte16(struct mtd_info *mtd)  { @@ -185,10 +179,9 @@ static uint8_t nand_read_byte16(struct mtd_info *mtd)  /**   * nand_read_word - [DEFAULT] read one word from the chip - * @mtd:	MTD device structure + * @mtd: MTD device structure   * - * Default read function for 16bit buswith without - * endianess conversion + * Default read function for 16bit buswidth without endianness conversion.   */  static u16 nand_read_word(struct mtd_info *mtd)  { @@ -198,8 +191,8 @@ static u16 nand_read_word(struct mtd_info *mtd)  /**   * nand_select_chip - [DEFAULT] control CE line - * @mtd:	MTD device structure - * @chipnr:	chipnumber to select, -1 for deselect + * @mtd: MTD device structure + * @chipnr: chipnumber to select, -1 for deselect   *   * Default select function for 1 chip devices.   */ @@ -221,11 +214,11 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr)  /**   * nand_write_buf - [DEFAULT] write buffer to chip - * @mtd:	MTD device structure - * @buf:	data buffer - * @len:	number of bytes to write + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write   * - * Default write function for 8bit buswith + * Default write function for 8bit buswidth.   */  void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)  { @@ -238,11 +231,11 @@ void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)  /**   * nand_read_buf - [DEFAULT] read chip data into buffer - * @mtd:	MTD device structure - * @buf:	buffer to store date - * @len:	number of bytes to read + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read   * - * Default read function for 8bit buswith + * Default read function for 8bit buswidth.   */  void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)  { @@ -255,11 +248,11 @@ void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)  /**   * nand_verify_buf - [DEFAULT] Verify chip data against buffer - * @mtd:	MTD device structure - * @buf:	buffer containing the data to compare - * @len:	number of bytes to compare + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare   * - * Default verify function for 8bit buswith + * Default verify function for 8bit buswidth.   */  static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)  { @@ -274,11 +267,11 @@ static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)  /**   * nand_write_buf16 - [DEFAULT] write buffer to chip - * @mtd:	MTD device structure - * @buf:	data buffer - * @len:	number of bytes to write + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write   * - * Default write function for 16bit buswith + * Default write function for 16bit buswidth.   */  void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)  { @@ -294,11 +287,11 @@ void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)  /**   * nand_read_buf16 - [DEFAULT] read chip data into buffer - * @mtd:	MTD device structure - * @buf:	buffer to store date - * @len:	number of bytes to read + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read   * - * Default read function for 16bit buswith + * Default read function for 16bit buswidth.   */  void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)  { @@ -313,11 +306,11 @@ void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)  /**   * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer - * @mtd:	MTD device structure - * @buf:	buffer containing the data to compare - * @len:	number of bytes to compare + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare   * - * Default verify function for 16bit buswith + * Default verify function for 16bit buswidth.   */  static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)  { @@ -335,19 +328,19 @@ static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)  /**   * nand_block_bad - [DEFAULT] Read bad block marker from the chip - * @mtd:	MTD device structure - * @ofs:	offset from device start - * @getchip:	0, if the chip is already selected + * @mtd: MTD device structure + * @ofs: offset from device start + * @getchip: 0, if the chip is already selected   *   * Check, if the block is bad.   */  static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)  { -	int page, chipnr, res = 0; +	int page, chipnr, res = 0, i = 0;  	struct nand_chip *chip = mtd->priv;  	u16 bad; -	if (chip->options & NAND_BBT_SCANLASTPAGE) +	if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)  		ofs += mtd->erasesize - mtd->writesize;  	page = (int)(ofs >> chip->page_shift) & chip->pagemask; @@ -361,23 +354,29 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)  		chip->select_chip(mtd, chipnr);  	} -	if (chip->options & NAND_BUSWIDTH_16) { -		chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE, -			      page); -		bad = cpu_to_le16(chip->read_word(mtd)); -		if (chip->badblockpos & 0x1) -			bad >>= 8; -		else -			bad &= 0xFF; -	} else { -		chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page); -		bad = chip->read_byte(mtd); -	} +	do { +		if (chip->options & NAND_BUSWIDTH_16) { +			chip->cmdfunc(mtd, NAND_CMD_READOOB, +					chip->badblockpos & 0xFE, page); +			bad = cpu_to_le16(chip->read_word(mtd)); +			if (chip->badblockpos & 0x1) +				bad >>= 8; +			else +				bad &= 0xFF; +		} else { +			chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, +					page); +			bad = chip->read_byte(mtd); +		} -	if (likely(chip->badblockbits == 8)) -		res = bad != 0xFF; -	else -		res = hweight8(bad) < chip->badblockbits; +		if (likely(chip->badblockbits == 8)) +			res = bad != 0xFF; +		else +			res = hweight8(bad) < chip->badblockbits; +		ofs += mtd->writesize; +		page = (int)(ofs >> chip->page_shift) & chip->pagemask; +		i++; +	} while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));  	if (getchip)  		nand_release_device(mtd); @@ -387,57 +386,83 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)  /**   * nand_default_block_markbad - [DEFAULT] mark a block bad - * @mtd:	MTD device structure - * @ofs:	offset from device start + * @mtd: MTD device structure + * @ofs: offset from device start   * - * This is the default implementation, which can be overridden by - * a hardware specific driver. + * This is the default implementation, which can be overridden by a hardware + * specific driver. We try operations in the following order, according to our + * bbt_options (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH): + *  (1) erase the affected block, to allow OOB marker to be written cleanly + *  (2) update in-memory BBT + *  (3) write bad block marker to OOB area of affected block + *  (4) update flash-based BBT + * Note that we retain the first error encountered in (3) or (4), finish the + * procedures, and dump the error in the end.  */  static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)  {  	struct nand_chip *chip = mtd->priv;  	uint8_t buf[2] = { 0, 0 }; -	int block, ret, i = 0; +	int block, res, ret = 0, i = 0; +	int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM); -	if (chip->options & NAND_BBT_SCANLASTPAGE) -		ofs += mtd->erasesize - mtd->writesize; +	if (write_oob) { +		struct erase_info einfo; + +		/* Attempt erase before marking OOB */ +		memset(&einfo, 0, sizeof(einfo)); +		einfo.mtd = mtd; +		einfo.addr = ofs; +		einfo.len = 1 << chip->phys_erase_shift; +		nand_erase_nand(mtd, &einfo, 0); +	}  	/* Get block number */  	block = (int)(ofs >> chip->bbt_erase_shift); +	/* Mark block bad in memory-based BBT */  	if (chip->bbt)  		chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); -	/* Do we have a flash based bad block table ? */ -	if (chip->options & NAND_USE_FLASH_BBT) -		ret = nand_update_bbt(mtd, ofs); -	else { +	/* Write bad block marker to OOB */ +	if (write_oob) { +		struct mtd_oob_ops ops; +		loff_t wr_ofs = ofs; +  		nand_get_device(chip, mtd, FL_WRITING); -		/* Write to first two pages and to byte 1 and 6 if necessary. -		 * If we write to more than one location, the first error -		 * encountered quits the procedure. We write two bytes per -		 * location, so we dont have to mess with 16 bit access. -		 */ -		do { -			chip->ops.len = chip->ops.ooblen = 2; -			chip->ops.datbuf = NULL; -			chip->ops.oobbuf = buf; -			chip->ops.ooboffs = chip->badblockpos & ~0x01; +		ops.datbuf = NULL; +		ops.oobbuf = buf; +		ops.ooboffs = chip->badblockpos; +		if (chip->options & NAND_BUSWIDTH_16) { +			ops.ooboffs &= ~0x01; +			ops.len = ops.ooblen = 2; +		} else { +			ops.len = ops.ooblen = 1; +		} +		ops.mode = MTD_OPS_PLACE_OOB; -			ret = nand_do_write_oob(mtd, ofs, &chip->ops); +		/* Write to first/last page(s) if necessary */ +		if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) +			wr_ofs += mtd->erasesize - mtd->writesize; +		do { +			res = nand_do_write_oob(mtd, wr_ofs, &ops); +			if (!ret) +				ret = res; -			if (!ret && (chip->options & NAND_BBT_SCANBYTE1AND6)) { -				chip->ops.ooboffs = NAND_SMALL_BADBLOCK_POS -					& ~0x01; -				ret = nand_do_write_oob(mtd, ofs, &chip->ops); -			}  			i++; -			ofs += mtd->writesize; -		} while (!ret && (chip->options & NAND_BBT_SCAN2NDPAGE) && -				i < 2); +			wr_ofs += mtd->writesize; +		} while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);  		nand_release_device(mtd);  	} + +	/* Update flash-based bad block table */ +	if (chip->bbt_options & NAND_BBT_USE_FLASH) { +		res = nand_update_bbt(mtd, ofs); +		if (!ret) +			ret = res; +	} +  	if (!ret)  		mtd->ecc_stats.badblocks++; @@ -446,16 +471,16 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)  /**   * nand_check_wp - [GENERIC] check if the chip is write protected - * @mtd:	MTD device structure - * Check, if the device is write protected + * @mtd: MTD device structure   * - * The function expects, that the device is already selected + * Check, if the device is write protected. The function expects, that the + * device is already selected.   */  static int nand_check_wp(struct mtd_info *mtd)  {  	struct nand_chip *chip = mtd->priv; -	/* broken xD cards report WP despite being writable */ +	/* Broken xD cards report WP despite being writable */  	if (chip->options & NAND_BROKEN_XD)  		return 0; @@ -466,10 +491,10 @@ static int nand_check_wp(struct mtd_info *mtd)  /**   * nand_block_checkbad - [GENERIC] Check if a block is marked bad - * @mtd:	MTD device structure - * @ofs:	offset from device start - * @getchip:	0, if the chip is already selected - * @allowbbt:	1, if its allowed to access the bbt area + * @mtd: MTD device structure + * @ofs: offset from device start + * @getchip: 0, if the chip is already selected + * @allowbbt: 1, if its allowed to access the bbt area   *   * Check, if the block is bad. Either by reading the bad block table or   * calling of the scan function. @@ -491,10 +516,7 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,  	return nand_isbad_bbt(mtd, ofs, allowbbt);  } -/* - * Wait for the ready pin, after a command - * The timeout is catched later. - */ +/* Wait for the ready pin, after a command. The timeout is caught later. */  void nand_wait_ready(struct mtd_info *mtd)  {  	struct nand_chip *chip = mtd->priv; @@ -503,7 +525,7 @@ void nand_wait_ready(struct mtd_info *mtd)  	time_start = get_timer(0); -	/* wait until command is processed or timeout occures */ +	/* Wait until command is processed or timeout occurs */  	while (get_timer(time_start) < timeo) {  		if (chip->dev_ready)  			if (chip->dev_ready(mtd)) @@ -513,13 +535,13 @@ void nand_wait_ready(struct mtd_info *mtd)  /**   * nand_command - [DEFAULT] Send command to NAND device - * @mtd:	MTD device structure - * @command:	the command to be sent - * @column:	the column address for this command, -1 if none - * @page_addr:	the page address for this command, -1 if none + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none   * - * Send command to NAND device. This function is used for small page - * devices (256/512 Bytes per page) + * Send command to NAND device. This function is used for small page devices + * (256/512 Bytes per page).   */  static void nand_command(struct mtd_info *mtd, unsigned int command,  			 int column, int page_addr) @@ -528,9 +550,7 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,  	int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;  	uint32_t rst_sts_cnt = CONFIG_SYS_NAND_RESET_CNT; -	/* -	 * Write out the command to the device. -	 */ +	/* Write out the command to the device */  	if (command == NAND_CMD_SEQIN) {  		int readcmd; @@ -550,9 +570,7 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,  	}  	chip->cmd_ctrl(mtd, command, ctrl); -	/* -	 * Address cycle, when necessary -	 */ +	/* Address cycle, when necessary */  	ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;  	/* Serially input address */  	if (column != -1) { @@ -573,8 +591,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,  	chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);  	/* -	 * program and erase have their own busy handlers -	 * status and sequential in needs no delay +	 * Program and erase have their own busy handlers status and sequential +	 * in needs no delay  	 */  	switch (command) { @@ -608,8 +626,10 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,  			return;  		}  	} -	/* Apply this short delay always to ensure that we do wait tWB in -	 * any case on any machine. */ +	/* +	 * Apply this short delay always to ensure that we do wait tWB in +	 * any case on any machine. +	 */  	ndelay(100);  	nand_wait_ready(mtd); @@ -617,14 +637,14 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,  /**   * nand_command_lp - [DEFAULT] Send command to NAND large page device - * @mtd:	MTD device structure - * @command:	the command to be sent - * @column:	the column address for this command, -1 if none - * @page_addr:	the page address for this command, -1 if none + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none   *   * Send command to NAND device. This is the version for the new large page - * devices We dont have the separate regions as we have in the small page - * devices.  We must emulate NAND_CMD_READOOB to keep the code compatible. + * devices. We don't have the separate regions as we have in the small page + * devices. We must emulate NAND_CMD_READOOB to keep the code compatible.   */  static void nand_command_lp(struct mtd_info *mtd, unsigned int command,  			    int column, int page_addr) @@ -667,8 +687,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,  	chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);  	/* -	 * program and erase have their own busy handlers -	 * status, sequential in, and deplete1 need no delay +	 * Program and erase have their own busy handlers status, sequential +	 * in, and deplete1 need no delay.  	 */  	switch (command) { @@ -682,14 +702,12 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,  	case NAND_CMD_DEPLETE1:  		return; -		/* -		 * read error status commands require only a short delay -		 */  	case NAND_CMD_STATUS_ERROR:  	case NAND_CMD_STATUS_ERROR0:  	case NAND_CMD_STATUS_ERROR1:  	case NAND_CMD_STATUS_ERROR2:  	case NAND_CMD_STATUS_ERROR3: +		/* Read error status commands require only a short delay */  		udelay(chip->chip_delay);  		return; @@ -723,7 +741,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,  	default:  		/*  		 * If we don't have access to the busy pin, we apply the given -		 * command delay +		 * command delay.  		 */  		if (!chip->dev_ready) {  			udelay(chip->chip_delay); @@ -731,8 +749,10 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,  		}  	} -	/* Apply this short delay always to ensure that we do wait tWB in -	 * any case on any machine. */ +	/* +	 * Apply this short delay always to ensure that we do wait tWB in +	 * any case on any machine. +	 */  	ndelay(100);  	nand_wait_ready(mtd); @@ -740,9 +760,9 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,  /**   * nand_get_device - [GENERIC] Get chip for selected access - * @chip:	the nand chip descriptor - * @mtd:	MTD device structure - * @new_state:	the state which is requested + * @chip: the nand chip descriptor + * @mtd: MTD device structure + * @new_state: the state which is requested   *   * Get the device and lock it for exclusive access   */ @@ -754,13 +774,13 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)  }  /** - * nand_wait - [DEFAULT]  wait until the command is done - * @mtd:	MTD device structure - * @chip:	NAND chip structure + * nand_wait - [DEFAULT] wait until the command is done + * @mtd: MTD device structure + * @chip: NAND chip structure   * - * Wait for command done. This applies to erase and program only - * Erase can take up to 400ms and program up to 20ms according to - * general NAND and SmartMedia specs + * Wait for command done. This applies to erase and program only. Erase can + * take up to 400ms and program up to 20ms according to general NAND and + * SmartMedia specs.   */  static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)  { @@ -804,34 +824,37 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)  }  /** - * nand_read_page_raw - [Intern] read raw page data without ecc - * @mtd:	mtd info structure - * @chip:	nand chip info structure - * @buf:	buffer to store read data - * @page:	page number to read + * nand_read_page_raw - [INTERN] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read   * - * Not for syndrome calculating ecc controllers, which use a special oob layout + * Not for syndrome calculating ECC controllers, which use a special oob layout.   */  static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, -			      uint8_t *buf, int page) +			      uint8_t *buf, int oob_required, int page)  {  	chip->read_buf(mtd, buf, mtd->writesize); -	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); +	if (oob_required) +		chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);  	return 0;  }  /** - * nand_read_page_raw_syndrome - [Intern] read raw page data without ecc - * @mtd:	mtd info structure - * @chip:	nand chip info structure - * @buf:	buffer to store read data - * @page:	page number to read + * nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read   *   * We need a special oob layout and handling even when OOB isn't used.   */  static int nand_read_page_raw_syndrome(struct mtd_info *mtd, -					struct nand_chip *chip, -					uint8_t *buf, int page) +				       struct nand_chip *chip, uint8_t *buf, +				       int oob_required, int page)  {  	int eccsize = chip->ecc.size;  	int eccbytes = chip->ecc.bytes; @@ -864,14 +887,15 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd,  }  /** - * nand_read_page_swecc - [REPLACABLE] software ecc based page read function - * @mtd:	mtd info structure - * @chip:	nand chip info structure - * @buf:	buffer to store read data - * @page:	page number to read + * nand_read_page_swecc - [REPLACEABLE] software ECC based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read   */  static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, -				uint8_t *buf, int page) +				uint8_t *buf, int oob_required, int page)  {  	int i, eccsize = chip->ecc.size;  	int eccbytes = chip->ecc.bytes; @@ -881,7 +905,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,  	uint8_t *ecc_code = chip->buffers->ecccode;  	uint32_t *eccpos = chip->ecc.layout->eccpos; -	chip->ecc.read_page_raw(mtd, chip, buf, page); +	chip->ecc.read_page_raw(mtd, chip, buf, 1, page);  	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)  		chip->ecc.calculate(mtd, p, &ecc_calc[i]); @@ -905,12 +929,12 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,  }  /** - * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function - * @mtd:	mtd info structure - * @chip:	nand chip info structure - * @data_offs:	offset of requested data within the page - * @readlen:	data length - * @bufpoi:	buffer to store read data + * nand_read_subpage - [REPLACEABLE] software ECC based sub-page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @data_offs: offset of requested data within the page + * @readlen: data length + * @bufpoi: buffer to store read data   */  static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,  			uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) @@ -923,12 +947,12 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,  	int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;  	int index = 0; -	/* Column address wihin the page aligned to ECC size (256bytes). */ +	/* Column address within the page aligned to ECC size (256bytes) */  	start_step = data_offs / chip->ecc.size;  	end_step = (data_offs + readlen - 1) / chip->ecc.size;  	num_steps = end_step - start_step + 1; -	/* Data size aligned to ECC ecc.size*/ +	/* Data size aligned to ECC ecc.size */  	datafrag_len = num_steps * chip->ecc.size;  	eccfrag_len = num_steps * chip->ecc.bytes; @@ -940,13 +964,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,  	p = bufpoi + data_col_addr;  	chip->read_buf(mtd, p, datafrag_len); -	/* Calculate  ECC */ +	/* Calculate ECC */  	for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)  		chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]); -	/* The performance is faster if to position offsets -	   according to ecc.pos. Let make sure here that -	   there are no gaps in ecc positions */ +	/* +	 * The performance is faster if we position offsets according to +	 * ecc.pos. Let's make sure that there are no gaps in ECC positions. +	 */  	for (i = 0; i < eccfrag_len - 1; i++) {  		if (eccpos[i + start_step * chip->ecc.bytes] + 1 !=  			eccpos[i + start_step * chip->ecc.bytes + 1]) { @@ -958,8 +983,10 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,  		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);  		chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);  	} else { -		/* send the command to read the particular ecc bytes */ -		/* take care about buswidth alignment in read_buf */ +		/* +		 * Send the command to read the particular ECC bytes take care +		 * about buswidth alignment in read_buf. +		 */  		index = start_step * chip->ecc.bytes;  		aligned_pos = eccpos[index] & ~(busw - 1); @@ -992,16 +1019,17 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,  }  /** - * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function - * @mtd:	mtd info structure - * @chip:	nand chip info structure - * @buf:	buffer to store read data - * @page:	page number to read + * nand_read_page_hwecc - [REPLACEABLE] hardware ECC based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read   * - * Not for syndrome calculating ecc controllers which need a special oob layout + * Not for syndrome calculating ECC controllers which need a special oob layout.   */  static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, -				uint8_t *buf, int page) +				uint8_t *buf, int oob_required, int page)  {  	int i, eccsize = chip->ecc.size;  	int eccbytes = chip->ecc.bytes; @@ -1037,21 +1065,21 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,  }  /** - * nand_read_page_hwecc_oob_first - [REPLACABLE] hw ecc, read oob first - * @mtd:	mtd info structure - * @chip:	nand chip info structure - * @buf:	buffer to store read data - * @page:	page number to read + * nand_read_page_hwecc_oob_first - [REPLACEABLE] hw ecc, read oob first + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read   * - * Hardware ECC for large page chips, require OOB to be read first. - * For this ECC mode, the write_page method is re-used from ECC_HW. - * These methods read/write ECC from the OOB area, unlike the - * ECC_HW_SYNDROME support with multiple ECC steps, follows the - * "infix ECC" scheme and reads/writes ECC from the data area, by - * overwriting the NAND manufacturer bad block markings. + * Hardware ECC for large page chips, require OOB to be read first. For this + * ECC mode, the write_page method is re-used from ECC_HW. These methods + * read/write ECC from the OOB area, unlike the ECC_HW_SYNDROME support with + * multiple ECC steps, follows the "infix ECC" scheme and reads/writes ECC from + * the data area, by overwriting the NAND manufacturer bad block markings.   */  static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, -	struct nand_chip *chip, uint8_t *buf, int page) +	struct nand_chip *chip, uint8_t *buf, int oob_required, int page)  {  	int i, eccsize = chip->ecc.size;  	int eccbytes = chip->ecc.bytes; @@ -1086,17 +1114,18 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,  }  /** - * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read - * @mtd:	mtd info structure - * @chip:	nand chip info structure - * @buf:	buffer to store read data - * @page:	page number to read + * nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read   * - * The hw generator calculates the error syndrome automatically. Therefor - * we need a special oob layout and handling. + * The hw generator calculates the error syndrome automatically. Therefore we + * need a special oob layout and handling.   */  static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, -				   uint8_t *buf, int page) +				   uint8_t *buf, int oob_required, int page)  {  	int i, eccsize = chip->ecc.size;  	int eccbytes = chip->ecc.bytes; @@ -1141,29 +1170,29 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,  }  /** - * nand_transfer_oob - [Internal] Transfer oob to client buffer - * @chip:	nand chip structure - * @oob:	oob destination address - * @ops:	oob ops structure - * @len:	size of oob to transfer + * nand_transfer_oob - [INTERN] Transfer oob to client buffer + * @chip: nand chip structure + * @oob: oob destination address + * @ops: oob ops structure + * @len: size of oob to transfer   */  static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,  				  struct mtd_oob_ops *ops, size_t len)  {  	switch (ops->mode) { -	case MTD_OOB_PLACE: -	case MTD_OOB_RAW: +	case MTD_OPS_PLACE_OOB: +	case MTD_OPS_RAW:  		memcpy(oob, chip->oob_poi + ops->ooboffs, len);  		return oob + len; -	case MTD_OOB_AUTO: { +	case MTD_OPS_AUTO_OOB: {  		struct nand_oobfree *free = chip->ecc.layout->oobfree;  		uint32_t boffs = 0, roffs = ops->ooboffs;  		size_t bytes = 0;  		for (; free->length && len; free++, len -= bytes) { -			/* Read request not from offset 0 ? */ +			/* Read request not from offset 0? */  			if (unlikely(roffs)) {  				if (roffs >= free->length) {  					roffs -= free->length; @@ -1189,26 +1218,23 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,  }  /** - * nand_do_read_ops - [Internal] Read data with ECC - * - * @mtd:	MTD device structure - * @from:	offset to read from - * @ops:	oob ops structure + * nand_do_read_ops - [INTERN] Read data with ECC + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob ops structure   *   * Internal function. Called with chip held.   */  static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,  			    struct mtd_oob_ops *ops)  { -	int chipnr, page, realpage, col, bytes, aligned; +	int chipnr, page, realpage, col, bytes, aligned, oob_required;  	struct nand_chip *chip = mtd->priv;  	struct mtd_ecc_stats stats; -	int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; -	int sndcmd = 1;  	int ret = 0;  	uint32_t readlen = ops->len;  	uint32_t oobreadlen = ops->ooblen; -	uint32_t max_oobsize = ops->mode == MTD_OOB_AUTO ? +	uint32_t max_oobsize = ops->mode == MTD_OPS_AUTO_OOB ?  		mtd->oobavail : mtd->oobsize;  	uint8_t *bufpoi, *oob, *buf; @@ -1225,6 +1251,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,  	buf = ops->datbuf;  	oob = ops->oobbuf; +	oob_required = oob ? 1 : 0;  	while (1) {  		WATCHDOG_RESET(); @@ -1232,41 +1259,46 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,  		bytes = min(mtd->writesize - col, readlen);  		aligned = (bytes == mtd->writesize); -		/* Is the current page in the buffer ? */ +		/* Is the current page in the buffer? */  		if (realpage != chip->pagebuf || oob) {  			bufpoi = aligned ? buf : chip->buffers->databuf; -			if (likely(sndcmd)) { -				chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); -				sndcmd = 0; -			} +			chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);  			/* Now read the page into the buffer */ -			if (unlikely(ops->mode == MTD_OOB_RAW)) -				ret = chip->ecc.read_page_raw(mtd, chip, -							      bufpoi, page); +			if (unlikely(ops->mode == MTD_OPS_RAW)) +				ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, +							      oob_required, +							      page);  			else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&  			    !oob)  				ret = chip->ecc.read_subpage(mtd, chip,  							col, bytes, bufpoi);  			else  				ret = chip->ecc.read_page(mtd, chip, bufpoi, -							  page); -			if (ret < 0) +							  oob_required, page); +			if (ret < 0) { +				if (!aligned) +					/* Invalidate page cache */ +					chip->pagebuf = -1;  				break; +			}  			/* Transfer not aligned data */  			if (!aligned) {  				if (!NAND_HAS_SUBPAGE_READ(chip) && !oob && -				    !(mtd->ecc_stats.failed - stats.failed)) +				    !(mtd->ecc_stats.failed - stats.failed) && +				    (ops->mode != MTD_OPS_RAW))  					chip->pagebuf = realpage; +				else +					/* Invalidate page cache */ +					chip->pagebuf = -1;  				memcpy(buf, chip->buffers->databuf + col, bytes);  			}  			buf += bytes;  			if (unlikely(oob)) { -  				int toread = min(oobreadlen, max_oobsize);  				if (toread) { @@ -1275,20 +1307,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,  					oobreadlen -= toread;  				}  			} - -			if (!(chip->options & NAND_NO_READRDY)) { -				/* -				 * Apply delay or wait for ready/busy pin. Do -				 * this before the AUTOINCR check, so no -				 * problems arise if a chip which does auto -				 * increment is marked as NOAUTOINCR by the -				 * board driver. -				 */ -				if (!chip->dev_ready) -					udelay(chip->chip_delay); -				else -					nand_wait_ready(mtd); -			}  		} else {  			memcpy(buf, chip->buffers->databuf + col, bytes);  			buf += bytes; @@ -1299,7 +1317,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,  		if (!readlen)  			break; -		/* For subsequent reads align to page boundary. */ +		/* For subsequent reads align to page boundary */  		col = 0;  		/* Increment page address */  		realpage++; @@ -1311,12 +1329,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,  			chip->select_chip(mtd, -1);  			chip->select_chip(mtd, chipnr);  		} - -		/* Check, if the chip supports auto page increment -		 * or if we have hit a block boundary. -		 */ -		if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) -			sndcmd = 1;  	}  	ops->retlen = ops->len - (size_t) readlen; @@ -1334,69 +1346,55 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,  /**   * nand_read - [MTD Interface] MTD compatibility function for nand_do_read_ecc - * @mtd:	MTD device structure - * @from:	offset to read from - * @len:	number of bytes to read - * @retlen:	pointer to variable to store the number of read bytes - * @buf:	the databuffer to put data + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data   * - * Get hold of the chip and call nand_do_read + * Get hold of the chip and call nand_do_read.   */  static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,  		     size_t *retlen, uint8_t *buf)  {  	struct nand_chip *chip = mtd->priv; +	struct mtd_oob_ops ops;  	int ret; -	/* Do not allow reads past end of device */ -	if ((from + len) > mtd->size) -		return -EINVAL; -	if (!len) -		return 0; -  	nand_get_device(chip, mtd, FL_READING); - -	chip->ops.len = len; -	chip->ops.datbuf = buf; -	chip->ops.oobbuf = NULL; - -	ret = nand_do_read_ops(mtd, from, &chip->ops); - -	*retlen = chip->ops.retlen; - +	ops.len = len; +	ops.datbuf = buf; +	ops.oobbuf = NULL; +	ops.mode = MTD_OPS_PLACE_OOB; +	ret = nand_do_read_ops(mtd, from, &ops); +	*retlen = ops.retlen;  	nand_release_device(mtd); -  	return ret;  }  /** - * nand_read_oob_std - [REPLACABLE] the most common OOB data read function - * @mtd:	mtd info structure - * @chip:	nand chip info structure - * @page:	page number to read - * @sndcmd:	flag whether to issue read command or not + * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read   */  static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, -			     int page, int sndcmd) +			     int page)  { -	if (sndcmd) { -		chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); -		sndcmd = 0; -	} +	chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);  	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); -	return sndcmd; +	return 0;  }  /** - * nand_read_oob_syndrome - [REPLACABLE] OOB data read function for HW ECC + * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC   *			    with syndromes - * @mtd:	mtd info structure - * @chip:	nand chip info structure - * @page:	page number to read - * @sndcmd:	flag whether to issue read command or not + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read   */  static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, -				  int page, int sndcmd) +				  int page)  {  	uint8_t *buf = chip->oob_poi;  	int length = mtd->oobsize; @@ -1423,14 +1421,14 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,  	if (length > 0)  		chip->read_buf(mtd, bufpoi, length); -	return 1; +	return 0;  }  /** - * nand_write_oob_std - [REPLACABLE] the most common OOB data write function - * @mtd:	mtd info structure - * @chip:	nand chip info structure - * @page:	page number to write + * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write   */  static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,  			      int page) @@ -1450,11 +1448,11 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,  }  /** - * nand_write_oob_syndrome - [REPLACABLE] OOB data write function for HW ECC - *			     with syndrome - only for large page flash ! - * @mtd:	mtd info structure - * @chip:	nand chip info structure - * @page:	page number to write + * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC + *			     with syndrome - only for large page flash + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write   */  static int nand_write_oob_syndrome(struct mtd_info *mtd,  				   struct nand_chip *chip, int page) @@ -1509,27 +1507,30 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd,  }  /** - * nand_do_read_oob - [Intern] NAND read out-of-band - * @mtd:	MTD device structure - * @from:	offset to read from - * @ops:	oob operations description structure + * nand_do_read_oob - [INTERN] NAND read out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operations description structure   * - * NAND read out-of-band data from the spare area + * NAND read out-of-band data from the spare area.   */  static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,  			    struct mtd_oob_ops *ops)  { -	int page, realpage, chipnr, sndcmd = 1; +	int page, realpage, chipnr;  	struct nand_chip *chip = mtd->priv; -	int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; +	struct mtd_ecc_stats stats;  	int readlen = ops->ooblen;  	int len;  	uint8_t *buf = ops->oobbuf; +	int ret = 0;  	MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n",  			__func__, (unsigned long long)from, readlen); -	if (ops->mode == MTD_OOB_AUTO) +	stats = mtd->ecc_stats; + +	if (ops->mode == MTD_OPS_AUTO_OOB)  		len = chip->ecc.layout->oobavail;  	else  		len = mtd->oobsize; @@ -1558,24 +1559,17 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,  	while (1) {  		WATCHDOG_RESET(); -		sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); +		if (ops->mode == MTD_OPS_RAW) +			ret = chip->ecc.read_oob_raw(mtd, chip, page); +		else +			ret = chip->ecc.read_oob(mtd, chip, page); + +		if (ret < 0) +			break;  		len = min(len, readlen);  		buf = nand_transfer_oob(chip, buf, ops, len); -		if (!(chip->options & NAND_NO_READRDY)) { -			/* -			 * Apply delay or wait for ready/busy pin. Do this -			 * before the AUTOINCR check, so no problems arise if a -			 * chip which does auto increment is marked as -			 * NOAUTOINCR by the board driver. -			 */ -			if (!chip->dev_ready) -				udelay(chip->chip_delay); -			else -				nand_wait_ready(mtd); -		} -  		readlen -= len;  		if (!readlen)  			break; @@ -1590,25 +1584,26 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,  			chip->select_chip(mtd, -1);  			chip->select_chip(mtd, chipnr);  		} - -		/* Check, if the chip supports auto page increment -		 * or if we have hit a block boundary. -		 */ -		if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) -			sndcmd = 1;  	} -	ops->oobretlen = ops->ooblen; -	return 0; +	ops->oobretlen = ops->ooblen - readlen; + +	if (ret < 0) +		return ret; + +	if (mtd->ecc_stats.failed - stats.failed) +		return -EBADMSG; + +	return  mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;  }  /**   * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band - * @mtd:	MTD device structure - * @from:	offset to read from - * @ops:	oob operation description structure + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operation description structure   * - * NAND read data and/or out-of-band data + * NAND read data and/or out-of-band data.   */  static int nand_read_oob(struct mtd_info *mtd, loff_t from,  			 struct mtd_oob_ops *ops) @@ -1628,9 +1623,9 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,  	nand_get_device(chip, mtd, FL_READING);  	switch (ops->mode) { -	case MTD_OOB_PLACE: -	case MTD_OOB_AUTO: -	case MTD_OOB_RAW: +	case MTD_OPS_PLACE_OOB: +	case MTD_OPS_AUTO_OOB: +	case MTD_OPS_RAW:  		break;  	default: @@ -1649,31 +1644,36 @@ out:  /** - * nand_write_page_raw - [Intern] raw page write function - * @mtd:	mtd info structure - * @chip:	nand chip info structure - * @buf:	data buffer + * nand_write_page_raw - [INTERN] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB   * - * Not for syndrome calculating ecc controllers, which use a special oob layout + * Not for syndrome calculating ECC controllers, which use a special oob layout.   */ -static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, -				const uint8_t *buf) +static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, +				const uint8_t *buf, int oob_required)  {  	chip->write_buf(mtd, buf, mtd->writesize); -	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); +	if (oob_required) +		chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + +	return 0;  }  /** - * nand_write_page_raw_syndrome - [Intern] raw page write function - * @mtd:	mtd info structure - * @chip:	nand chip info structure - * @buf:	data buffer + * nand_write_page_raw_syndrome - [INTERN] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB   *   * We need a special oob layout and handling even when ECC isn't checked.   */ -static void nand_write_page_raw_syndrome(struct mtd_info *mtd, +static int nand_write_page_raw_syndrome(struct mtd_info *mtd,  					struct nand_chip *chip, -					const uint8_t *buf) +					const uint8_t *buf, int oob_required)  {  	int eccsize = chip->ecc.size;  	int eccbytes = chip->ecc.bytes; @@ -1701,15 +1701,18 @@ static void nand_write_page_raw_syndrome(struct mtd_info *mtd,  	size = mtd->oobsize - (oob - chip->oob_poi);  	if (size)  		chip->write_buf(mtd, oob, size); + +	return 0;  }  /** - * nand_write_page_swecc - [REPLACABLE] software ecc based page write function - * @mtd:	mtd info structure - * @chip:	nand chip info structure - * @buf:	data buffer + * nand_write_page_swecc - [REPLACEABLE] software ECC based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB   */ -static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, -				  const uint8_t *buf) +static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, +				  const uint8_t *buf, int oob_required)  {  	int i, eccsize = chip->ecc.size;  	int eccbytes = chip->ecc.bytes; @@ -1718,24 +1721,25 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,  	const uint8_t *p = buf;  	uint32_t *eccpos = chip->ecc.layout->eccpos; -	/* Software ecc calculation */ +	/* Software ECC calculation */  	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)  		chip->ecc.calculate(mtd, p, &ecc_calc[i]);  	for (i = 0; i < chip->ecc.total; i++)  		chip->oob_poi[eccpos[i]] = ecc_calc[i]; -	chip->ecc.write_page_raw(mtd, chip, buf); +	return chip->ecc.write_page_raw(mtd, chip, buf, 1);  }  /** - * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function - * @mtd:	mtd info structure - * @chip:	nand chip info structure - * @buf:	data buffer + * nand_write_page_hwecc - [REPLACEABLE] hardware ECC based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB   */ -static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, -				  const uint8_t *buf) +static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, +				  const uint8_t *buf, int oob_required)  {  	int i, eccsize = chip->ecc.size;  	int eccbytes = chip->ecc.bytes; @@ -1754,19 +1758,23 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,  		chip->oob_poi[eccpos[i]] = ecc_calc[i];  	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + +	return 0;  }  /** - * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write - * @mtd:	mtd info structure - * @chip:	nand chip info structure - * @buf:	data buffer + * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB   * - * The hw generator calculates the error syndrome automatically. Therefor - * we need a special oob layout and handling. + * The hw generator calculates the error syndrome automatically. Therefore we + * need a special oob layout and handling.   */ -static void nand_write_page_syndrome(struct mtd_info *mtd, -				    struct nand_chip *chip, const uint8_t *buf) +static int nand_write_page_syndrome(struct mtd_info *mtd, +				    struct nand_chip *chip, +				    const uint8_t *buf, int oob_required)  {  	int i, eccsize = chip->ecc.size;  	int eccbytes = chip->ecc.bytes; @@ -1798,32 +1806,39 @@ static void nand_write_page_syndrome(struct mtd_info *mtd,  	i = mtd->oobsize - (oob - chip->oob_poi);  	if (i)  		chip->write_buf(mtd, oob, i); + +	return 0;  }  /**   * nand_write_page - [REPLACEABLE] write one page - * @mtd:	MTD device structure - * @chip:	NAND chip descriptor - * @buf:	the data to write - * @page:	page number to write - * @cached:	cached programming - * @raw:	use _raw version of write_page + * @mtd: MTD device structure + * @chip: NAND chip descriptor + * @buf: the data to write + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + * @cached: cached programming + * @raw: use _raw version of write_page   */  static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, -			   const uint8_t *buf, int page, int cached, int raw) +			   const uint8_t *buf, int oob_required, int page, +			   int cached, int raw)  {  	int status;  	chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);  	if (unlikely(raw)) -		chip->ecc.write_page_raw(mtd, chip, buf); +		status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required);  	else -		chip->ecc.write_page(mtd, chip, buf); +		status = chip->ecc.write_page(mtd, chip, buf, oob_required); + +	if (status < 0) +		return status;  	/* -	 * Cached progamming disabled for now, Not sure if its worth the -	 * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) +	 * Cached progamming disabled for now. Not sure if it's worth the +	 * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s).  	 */  	cached = 0; @@ -1833,7 +1848,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,  		status = chip->waitfunc(mtd, chip);  		/*  		 * See if operation failed and additional status checks are -		 * available +		 * available.  		 */  		if ((status & NAND_STATUS_FAIL) && (chip->errstat))  			status = chip->errstat(mtd, chip, FL_WRITING, status, @@ -1852,34 +1867,45 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,  	if (chip->verify_buf(mtd, buf, mtd->writesize))  		return -EIO; + +	/* Make sure the next page prog is preceded by a status read */ +	chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);  #endif  	return 0;  }  /** - * nand_fill_oob - [Internal] Transfer client buffer to oob - * @chip:	nand chip structure - * @oob:	oob data buffer - * @len:	oob data write length - * @ops:	oob ops structure + * nand_fill_oob - [INTERN] Transfer client buffer to oob + * @mtd: MTD device structure + * @oob: oob data buffer + * @len: oob data write length + * @ops: oob ops structure   */ -static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len, -						struct mtd_oob_ops *ops) +static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len, +			      struct mtd_oob_ops *ops)  { +	struct nand_chip *chip = mtd->priv; + +	/* +	 * Initialise to all 0xFF, to avoid the possibility of left over OOB +	 * data from a previous OOB read. +	 */ +	memset(chip->oob_poi, 0xff, mtd->oobsize); +  	switch (ops->mode) { -	case MTD_OOB_PLACE: -	case MTD_OOB_RAW: +	case MTD_OPS_PLACE_OOB: +	case MTD_OPS_RAW:  		memcpy(chip->oob_poi + ops->ooboffs, oob, len);  		return oob + len; -	case MTD_OOB_AUTO: { +	case MTD_OPS_AUTO_OOB: {  		struct nand_oobfree *free = chip->ecc.layout->oobfree;  		uint32_t boffs = 0, woffs = ops->ooboffs;  		size_t bytes = 0;  		for (; free->length && len; free++, len -= bytes) { -			/* Write request not from offset 0 ? */ +			/* Write request not from offset 0? */  			if (unlikely(woffs)) {  				if (woffs >= free->length) {  					woffs -= free->length; @@ -1907,12 +1933,12 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,  #define NOTALIGNED(x)	((x & (chip->subpagesize - 1)) != 0)  /** - * nand_do_write_ops - [Internal] NAND write with ECC - * @mtd:	MTD device structure - * @to:		offset to write to - * @ops:	oob operations description structure + * nand_do_write_ops - [INTERN] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operations description structure   * - * NAND write with ECC + * NAND write with ECC.   */  static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,  			     struct mtd_oob_ops *ops) @@ -1922,12 +1948,13 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,  	uint32_t writelen = ops->len;  	uint32_t oobwritelen = ops->ooblen; -	uint32_t oobmaxlen = ops->mode == MTD_OOB_AUTO ? +	uint32_t oobmaxlen = ops->mode == MTD_OPS_AUTO_OOB ?  				mtd->oobavail : mtd->oobsize;  	uint8_t *oob = ops->oobbuf;  	uint8_t *buf = ops->datbuf;  	int ret, subpage; +	int oob_required = oob ? 1 : 0;  	ops->retlen = 0;  	if (!writelen) @@ -1957,10 +1984,6 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,  	    (chip->pagebuf << chip->page_shift) < (to + ops->len))  		chip->pagebuf = -1; -	/* If we're not given explicit OOB data, let it be 0xFF */ -	if (likely(!oob)) -		memset(chip->oob_poi, 0xff, mtd->oobsize); -  	/* Don't allow multipage oob writes with offset */  	if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen))  		return -EINVAL; @@ -1972,8 +1995,8 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,  		int cached = writelen > bytes && page != blockmask;  		uint8_t *wbuf = buf; -		/* Partial page write ? */ -		if (unlikely(column || writelen < (mtd->writesize - 1))) { +		/* Partial page write? */ +		if (unlikely(column || writelen < mtd->writesize)) {  			cached = 0;  			bytes = min_t(int, bytes - column, (int) writelen);  			chip->pagebuf = -1; @@ -1984,12 +2007,15 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,  		if (unlikely(oob)) {  			size_t len = min(oobwritelen, oobmaxlen); -			oob = nand_fill_oob(chip, oob, len, ops); +			oob = nand_fill_oob(mtd, oob, len, ops);  			oobwritelen -= len; +		} else { +			/* We still need to erase leftover OOB data */ +			memset(chip->oob_poi, 0xff, mtd->oobsize);  		} -		ret = chip->write_page(mtd, chip, wbuf, page, cached, -				       (ops->mode == MTD_OOB_RAW)); +		ret = chip->write_page(mtd, chip, wbuf, oob_required, page, +				       cached, (ops->mode == MTD_OPS_RAW));  		if (ret)  			break; @@ -2018,48 +2044,39 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,  /**   * nand_write - [MTD Interface] NAND write with ECC - * @mtd:	MTD device structure - * @to:		offset to write to - * @len:	number of bytes to write - * @retlen:	pointer to variable to store the number of written bytes - * @buf:	the data to write + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write   * - * NAND write with ECC + * NAND write with ECC.   */  static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,  			  size_t *retlen, const uint8_t *buf)  {  	struct nand_chip *chip = mtd->priv; +	struct mtd_oob_ops ops;  	int ret; -	/* Do not allow writes past end of device */ -	if ((to + len) > mtd->size) -		return -EINVAL; -	if (!len) -		return 0; -  	nand_get_device(chip, mtd, FL_WRITING); - -	chip->ops.len = len; -	chip->ops.datbuf = (uint8_t *)buf; -	chip->ops.oobbuf = NULL; - -	ret = nand_do_write_ops(mtd, to, &chip->ops); - -	*retlen = chip->ops.retlen; - +	ops.len = len; +	ops.datbuf = (uint8_t *)buf; +	ops.oobbuf = NULL; +	ops.mode = MTD_OPS_PLACE_OOB; +	ret = nand_do_write_ops(mtd, to, &ops); +	*retlen = ops.retlen;  	nand_release_device(mtd); -  	return ret;  }  /**   * nand_do_write_oob - [MTD Interface] NAND write out-of-band - * @mtd:	MTD device structure - * @to:		offset to write to - * @ops:	oob operation description structure + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure   * - * NAND write out-of-band + * NAND write out-of-band.   */  static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,  			     struct mtd_oob_ops *ops) @@ -2070,7 +2087,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,  	MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n",  			 __func__, (unsigned int)to, (int)ops->ooblen); -	if (ops->mode == MTD_OOB_AUTO) +	if (ops->mode == MTD_OPS_AUTO_OOB)  		len = chip->ecc.layout->oobavail;  	else  		len = mtd->oobsize; @@ -2120,10 +2137,12 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,  	if (page == chip->pagebuf)  		chip->pagebuf = -1; -	memset(chip->oob_poi, 0xff, mtd->oobsize); -	nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops); -	status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); -	memset(chip->oob_poi, 0xff, mtd->oobsize); +	nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops); + +	if (ops->mode == MTD_OPS_RAW) +		status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask); +	else +		status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);  	if (status)  		return status; @@ -2135,9 +2154,9 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,  /**   * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band - * @mtd:	MTD device structure - * @to:		offset to write to - * @ops:	oob operation description structure + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure   */  static int nand_write_oob(struct mtd_info *mtd, loff_t to,  			  struct mtd_oob_ops *ops) @@ -2157,9 +2176,9 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,  	nand_get_device(chip, mtd, FL_WRITING);  	switch (ops->mode) { -	case MTD_OOB_PLACE: -	case MTD_OOB_AUTO: -	case MTD_OOB_RAW: +	case MTD_OPS_PLACE_OOB: +	case MTD_OPS_AUTO_OOB: +	case MTD_OPS_RAW:  		break;  	default: @@ -2177,11 +2196,11 @@ out:  }  /** - * single_erease_cmd - [GENERIC] NAND standard block erase command function - * @mtd:	MTD device structure - * @page:	the page address of the block which will be erased + * single_erase_cmd - [GENERIC] NAND standard block erase command function + * @mtd: MTD device structure + * @page: the page address of the block which will be erased   * - * Standard erase command for NAND chips + * Standard erase command for NAND chips.   */  static void single_erase_cmd(struct mtd_info *mtd, int page)  { @@ -2192,12 +2211,11 @@ static void single_erase_cmd(struct mtd_info *mtd, int page)  }  /** - * multi_erease_cmd - [GENERIC] AND specific block erase command function - * @mtd:	MTD device structure - * @page:	the page address of the block which will be erased + * multi_erase_cmd - [GENERIC] AND specific block erase command function + * @mtd: MTD device structure + * @page: the page address of the block which will be erased   * - * AND multi block erase command function - * Erase 4 consecutive blocks + * AND multi block erase command function. Erase 4 consecutive blocks.   */  static void multi_erase_cmd(struct mtd_info *mtd, int page)  { @@ -2212,10 +2230,10 @@ static void multi_erase_cmd(struct mtd_info *mtd, int page)  /**   * nand_erase - [MTD Interface] erase block(s) - * @mtd:	MTD device structure - * @instr:	erase instruction + * @mtd: MTD device structure + * @instr: erase instruction   * - * Erase one ore more blocks + * Erase one ore more blocks.   */  static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)  { @@ -2224,12 +2242,12 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)  #define BBT_PAGE_MASK	0xffffff3f  /** - * nand_erase_nand - [Internal] erase block(s) - * @mtd:	MTD device structure - * @instr:	erase instruction - * @allowbbt:	allow erasing the bbt area + * nand_erase_nand - [INTERN] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * @allowbbt: allow erasing the bbt area   * - * Erase one ore more blocks + * Erase one ore more blocks.   */  int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,  		    int allowbbt) @@ -2247,8 +2265,6 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,  	if (check_offs_len(mtd, instr->addr, instr->len))  		return -EINVAL; -	instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; -  	/* Grab the lock and see if the device is available */  	nand_get_device(chip, mtd, FL_ERASING); @@ -2274,7 +2290,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,  	 * If BBT requires refresh, set the BBT page mask to see if the BBT  	 * should be rewritten. Otherwise the mask is set to 0xffffffff which  	 * can not be matched. This is also done when the bbt is actually -	 * erased to avoid recusrsive updates +	 * erased to avoid recursive updates.  	 */  	if (chip->options & BBT_AUTO_REFRESH && !allowbbt)  		bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK; @@ -2286,20 +2302,18 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,  	while (len) {  		WATCHDOG_RESET(); -		/* -		 * heck if we have a bad block, we do not erase bad blocks ! -		 */ +		/* Check if we have a bad block, we do not erase bad blocks! */  		if (!instr->scrub && nand_block_checkbad(mtd, ((loff_t) page) <<  					chip->page_shift, 0, allowbbt)) { -			printk(KERN_WARNING "%s: attempt to erase a bad block " -					"at page 0x%08x\n", __func__, page); +			pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", +				   __func__, page);  			instr->state = MTD_ERASE_FAILED;  			goto erase_exit;  		}  		/*  		 * Invalidate the page cache, if we erase the block which -		 * contains the current cached page +		 * contains the current cached page.  		 */  		if (page <= chip->pagebuf && chip->pagebuf <  		    (page + pages_per_block)) @@ -2329,7 +2343,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,  		/*  		 * If BBT requires refresh, set the BBT rewrite flag to the -		 * page being erased +		 * page being erased.  		 */  		if (bbt_masked_page != 0xffffffff &&  		    (page & BBT_PAGE_MASK) == bbt_masked_page) @@ -2348,7 +2362,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,  			/*  			 * If BBT requires refresh and BBT-PERCHIP, set the BBT -			 * page mask to see if this BBT should be rewritten +			 * page mask to see if this BBT should be rewritten.  			 */  			if (bbt_masked_page != 0xffffffff &&  			    (chip->bbt_td->options & NAND_BBT_PERCHIP)) @@ -2371,7 +2385,7 @@ erase_exit:  	/*  	 * If BBT requires refresh and erase was successful, rewrite any -	 * selected bad block tables +	 * selected bad block tables.  	 */  	if (bbt_masked_page == 0xffffffff || ret)  		return ret; @@ -2379,7 +2393,7 @@ erase_exit:  	for (chipnr = 0; chipnr < chip->numchips; chipnr++) {  		if (!rewrite_bbt[chipnr])  			continue; -		/* update the BBT for chip */ +		/* Update the BBT for chip */  		MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: nand_update_bbt "  			"(%d:0x%0llx 0x%0x)\n", __func__, chipnr,  			rewrite_bbt[chipnr], chip->bbt_td->pages[chipnr]); @@ -2392,9 +2406,9 @@ erase_exit:  /**   * nand_sync - [MTD Interface] sync - * @mtd:	MTD device structure + * @mtd: MTD device structure   * - * Sync is actually a wait for chip ready function + * Sync is actually a wait for chip ready function.   */  static void nand_sync(struct mtd_info *mtd)  { @@ -2410,22 +2424,18 @@ static void nand_sync(struct mtd_info *mtd)  /**   * nand_block_isbad - [MTD Interface] Check if block at offset is bad - * @mtd:	MTD device structure - * @offs:	offset relative to mtd start + * @mtd: MTD device structure + * @offs: offset relative to mtd start   */  static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)  { -	/* Check for invalid offset */ -	if (offs > mtd->size) -		return -EINVAL; -  	return nand_block_checkbad(mtd, offs, 1, 0);  }  /**   * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad - * @mtd:	MTD device structure - * @ofs:	offset relative to mtd start + * @mtd: MTD device structure + * @ofs: offset relative to mtd start   */  static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)  { @@ -2434,7 +2444,7 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)  	ret = nand_block_isbad(mtd, ofs);  	if (ret) { -		/* If it was bad already, return success and do nothing. */ +		/* If it was bad already, return success and do nothing */  		if (ret > 0)  			return 0;  		return ret; @@ -2443,9 +2453,51 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)  	return chip->block_markbad(mtd, ofs);  } -/* - * Set default functions + /** + * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand + * @mtd: MTD device structure + * @chip: nand chip info structure + * @addr: feature address. + * @subfeature_param: the subfeature parameters, a four bytes array. + */ +static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, +			int addr, uint8_t *subfeature_param) +{ +	int status; + +	if (!chip->onfi_version) +		return -EINVAL; + +	chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1); +	chip->write_buf(mtd, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN); +	status = chip->waitfunc(mtd, chip); +	if (status & NAND_STATUS_FAIL) +		return -EIO; +	return 0; +} + +/** + * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand + * @mtd: MTD device structure + * @chip: nand chip info structure + * @addr: feature address. + * @subfeature_param: the subfeature parameters, a four bytes array.   */ +static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, +			int addr, uint8_t *subfeature_param) +{ +	if (!chip->onfi_version) +		return -EINVAL; + +	/* clear the sub feature parameters */ +	memset(subfeature_param, 0, ONFI_SUBFEATURE_PARAM_LEN); + +	chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1); +	chip->read_buf(mtd, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN); +	return 0; +} + +/* Set default functions */  static void nand_set_defaults(struct nand_chip *chip, int busw)  {  	/* check for proper chip_delay setup, set 20us if not */ @@ -2483,23 +2535,21 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)  }  #ifdef CONFIG_SYS_NAND_ONFI_DETECTION -/* - * sanitize ONFI strings so we can safely print them - */ +/* Sanitize ONFI strings so we can safely print them */  static void sanitize_string(char *s, size_t len)  {  	ssize_t i; -	/* null terminate */ +	/* Null terminate */  	s[len - 1] = 0; -	/* remove non printable chars */ +	/* Remove non printable chars */  	for (i = 0; i < len - 1; i++) {  		if (s[i] < ' ' || s[i] > 127)  			s[i] = '?';  	} -	/* remove trailing spaces */ +	/* Remove trailing spaces */  	strim(s);  } @@ -2516,7 +2566,7 @@ static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)  }  /* - * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise + * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.   */  static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,  					int *busw) @@ -2525,20 +2575,18 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,  	int i;  	int val; -	/* try ONFI for unknow chip or LP */ +	/* Try ONFI for unknown chip or LP */  	chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);  	if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||  		chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')  		return 0; -	MTDDEBUG(MTD_DEBUG_LEVEL0, "ONFI flash detected\n");  	chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);  	for (i = 0; i < 3; i++) {  		chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));  		if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==  				le16_to_cpu(p->crc)) { -			MTDDEBUG(MTD_DEBUG_LEVEL0, -				 "ONFI param page %d valid\n", i); +			pr_info("ONFI param page %d valid\n", i);  			break;  		}  	} @@ -2546,7 +2594,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,  	if (i == 3)  		return 0; -	/* check version */ +	/* Check version */  	val = le16_to_cpu(p->revision);  	if (val & (1 << 5))  		chip->onfi_version = 23; @@ -2562,8 +2610,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,  		chip->onfi_version = 0;  	if (!chip->onfi_version) { -		printk(KERN_INFO "%s: unsupported ONFI version: %d\n", -								__func__, val); +		pr_info("%s: unsupported ONFI version: %d\n", __func__, val);  		return 0;  	} @@ -2580,8 +2627,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,  	if (le16_to_cpu(p->features) & 1)  		*busw = NAND_BUSWIDTH_16; -	chip->options |= NAND_NO_READRDY | NAND_NO_AUTOINCR; - +	pr_info("ONFI flash detected\n");  	return 1;  }  #else @@ -2594,7 +2640,248 @@ static inline int nand_flash_detect_onfi(struct mtd_info *mtd,  #endif  /* - * Get the flash and manufacturer id and lookup if the type is supported + * nand_id_has_period - Check if an ID string has a given wraparound period + * @id_data: the ID string + * @arrlen: the length of the @id_data array + * @period: the period of repitition + * + * Check if an ID string is repeated within a given sequence of bytes at + * specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a + * period of 2). This is a helper function for nand_id_len(). Returns non-zero + * if the repetition has a period of @period; otherwise, returns zero. + */ +static int nand_id_has_period(u8 *id_data, int arrlen, int period) +{ +	int i, j; +	for (i = 0; i < period; i++) +		for (j = i + period; j < arrlen; j += period) +			if (id_data[i] != id_data[j]) +				return 0; +	return 1; +} + +/* + * nand_id_len - Get the length of an ID string returned by CMD_READID + * @id_data: the ID string + * @arrlen: the length of the @id_data array + + * Returns the length of the ID string, according to known wraparound/trailing + * zero patterns. If no pattern exists, returns the length of the array. + */ +static int nand_id_len(u8 *id_data, int arrlen) +{ +	int last_nonzero, period; + +	/* Find last non-zero byte */ +	for (last_nonzero = arrlen - 1; last_nonzero >= 0; last_nonzero--) +		if (id_data[last_nonzero]) +			break; + +	/* All zeros */ +	if (last_nonzero < 0) +		return 0; + +	/* Calculate wraparound period */ +	for (period = 1; period < arrlen; period++) +		if (nand_id_has_period(id_data, arrlen, period)) +			break; + +	/* There's a repeated pattern */ +	if (period < arrlen) +		return period; + +	/* There are trailing zeros */ +	if (last_nonzero < arrlen - 1) +		return last_nonzero + 1; + +	/* No pattern detected */ +	return arrlen; +} + +/* + * Many new NAND share similar device ID codes, which represent the size of the + * chip. The rest of the parameters must be decoded according to generic or + * manufacturer-specific "extended ID" decoding patterns. + */ +static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, +				u8 id_data[8], int *busw) +{ +	int extid, id_len; +	/* The 3rd id byte holds MLC / multichip data */ +	chip->cellinfo = id_data[2]; +	/* The 4th id byte is the important one */ +	extid = id_data[3]; + +	id_len = nand_id_len(id_data, 8); + +	/* +	 * Field definitions are in the following datasheets: +	 * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32) +	 * New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44) +	 * Hynix MLC   (6 byte ID): Hynix H27UBG8T2B (p.22) +	 * +	 * Check for ID length, non-zero 6th byte, cell type, and Hynix/Samsung +	 * ID to decide what to do. +	 */ +	if (id_len == 6 && id_data[0] == NAND_MFR_SAMSUNG && +			(chip->cellinfo & NAND_CI_CELLTYPE_MSK) && +			id_data[5] != 0x00) { +		/* Calc pagesize */ +		mtd->writesize = 2048 << (extid & 0x03); +		extid >>= 2; +		/* Calc oobsize */ +		switch (((extid >> 2) & 0x04) | (extid & 0x03)) { +		case 1: +			mtd->oobsize = 128; +			break; +		case 2: +			mtd->oobsize = 218; +			break; +		case 3: +			mtd->oobsize = 400; +			break; +		case 4: +			mtd->oobsize = 436; +			break; +		case 5: +			mtd->oobsize = 512; +			break; +		case 6: +		default: /* Other cases are "reserved" (unknown) */ +			mtd->oobsize = 640; +			break; +		} +		extid >>= 2; +		/* Calc blocksize */ +		mtd->erasesize = (128 * 1024) << +			(((extid >> 1) & 0x04) | (extid & 0x03)); +		*busw = 0; +	} else if (id_len == 6 && id_data[0] == NAND_MFR_HYNIX && +			(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { +		unsigned int tmp; + +		/* Calc pagesize */ +		mtd->writesize = 2048 << (extid & 0x03); +		extid >>= 2; +		/* Calc oobsize */ +		switch (((extid >> 2) & 0x04) | (extid & 0x03)) { +		case 0: +			mtd->oobsize = 128; +			break; +		case 1: +			mtd->oobsize = 224; +			break; +		case 2: +			mtd->oobsize = 448; +			break; +		case 3: +			mtd->oobsize = 64; +			break; +		case 4: +			mtd->oobsize = 32; +			break; +		case 5: +			mtd->oobsize = 16; +			break; +		default: +			mtd->oobsize = 640; +			break; +		} +		extid >>= 2; +		/* Calc blocksize */ +		tmp = ((extid >> 1) & 0x04) | (extid & 0x03); +		if (tmp < 0x03) +			mtd->erasesize = (128 * 1024) << tmp; +		else if (tmp == 0x03) +			mtd->erasesize = 768 * 1024; +		else +			mtd->erasesize = (64 * 1024) << tmp; +		*busw = 0; +	} else { +		/* Calc pagesize */ +		mtd->writesize = 1024 << (extid & 0x03); +		extid >>= 2; +		/* Calc oobsize */ +		mtd->oobsize = (8 << (extid & 0x01)) * +			(mtd->writesize >> 9); +		extid >>= 2; +		/* Calc blocksize. Blocksize is multiples of 64KiB */ +		mtd->erasesize = (64 * 1024) << (extid & 0x03); +		extid >>= 2; +		/* Get buswidth information */ +		*busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; +	} +} + + /* + * Old devices have chip data hardcoded in the device ID table. nand_decode_id + * decodes a matching ID table entry and assigns the MTD size parameters for + * the chip. + */ +static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip, +				const struct nand_flash_dev *type, u8 id_data[8], +				int *busw) +{ +	int maf_id = id_data[0]; + +	mtd->erasesize = type->erasesize; +	mtd->writesize = type->pagesize; +	mtd->oobsize = mtd->writesize / 32; +	*busw = type->options & NAND_BUSWIDTH_16; + +	/* +	 * Check for Spansion/AMD ID + repeating 5th, 6th byte since +	 * some Spansion chips have erasesize that conflicts with size +	 * listed in nand_ids table. +	 * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39) +	 */ +	if (maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && id_data[5] == 0x00 +			&& id_data[6] == 0x00 && id_data[7] == 0x00 +			&& mtd->writesize == 512) { +		mtd->erasesize = 128 * 1024; +		mtd->erasesize <<= ((id_data[3] & 0x03) << 1); +	} +} + + /* + * Set the bad block marker/indicator (BBM/BBI) patterns according to some + * heuristic patterns using various detected parameters (e.g., manufacturer, + * page size, cell-type information). + */ +static void nand_decode_bbm_options(struct mtd_info *mtd, +				    struct nand_chip *chip, u8 id_data[8]) +{ +	int maf_id = id_data[0]; + +	/* Set the bad block position */ +	if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16)) +		chip->badblockpos = NAND_LARGE_BADBLOCK_POS; +	else +		chip->badblockpos = NAND_SMALL_BADBLOCK_POS; + +	/* +	 * Bad block marker is stored in the last page of each block on Samsung +	 * and Hynix MLC devices; stored in first two pages of each block on +	 * Micron devices with 2KiB pages and on SLC Samsung, Hynix, Toshiba, +	 * AMD/Spansion, and Macronix.  All others scan only the first page. +	 */ +	if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) && +			(maf_id == NAND_MFR_SAMSUNG || +			 maf_id == NAND_MFR_HYNIX)) +		chip->bbt_options |= NAND_BBT_SCANLASTPAGE; +	else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) && +				(maf_id == NAND_MFR_SAMSUNG || +				 maf_id == NAND_MFR_HYNIX || +				 maf_id == NAND_MFR_TOSHIBA || +				 maf_id == NAND_MFR_AMD || +				 maf_id == NAND_MFR_MACRONIX)) || +			(mtd->writesize == 2048 && +			 maf_id == NAND_MFR_MICRON)) +		chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; +} + +/* + * Get the flash and manufacturer id and lookup if the type is supported.   */  static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,  						  struct nand_chip *chip, @@ -2605,14 +2892,13 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,  	const char *name;  	int i, maf_idx;  	u8 id_data[8]; -	int ret;  	/* Select the device */  	chip->select_chip(mtd, 0);  	/*  	 * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx) -	 * after power-up +	 * after power-up.  	 */  	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); @@ -2623,7 +2909,8 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,  	*maf_id = chip->read_byte(mtd);  	*dev_id = chip->read_byte(mtd); -	/* Try again to make sure, as some systems the bus-hold or other +	/* +	 * Try again to make sure, as some systems the bus-hold or other  	 * interface concerns can cause random data which looks like a  	 * possibly credible NAND flash to appear. If the two results do  	 * not match, ignore the device completely. @@ -2631,13 +2918,14 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,  	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); -	for (i = 0; i < 2; i++) +	/* Read entire ID string */ +	for (i = 0; i < 8; i++)  		id_data[i] = chip->read_byte(mtd);  	if (id_data[0] != *maf_id || id_data[1] != *dev_id) { -		printk(KERN_INFO "%s: second ID read did not match " -		       "%02x,%02x against %02x,%02x\n", __func__, -		       *maf_id, *dev_id, id_data[0], id_data[1]); +		pr_info("%s: second ID read did not match " +			"%02x,%02x against %02x,%02x\n", __func__, +			*maf_id, *dev_id, id_data[0], id_data[1]);  		return ERR_PTR(-ENODEV);  	} @@ -2651,18 +2939,10 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,  	chip->onfi_version = 0;  	if (!type->name || !type->pagesize) {  		/* Check is chip is ONFI compliant */ -		ret = nand_flash_detect_onfi(mtd, chip, &busw); -		if (ret) +		if (nand_flash_detect_onfi(mtd, chip, &busw))  			goto ident_done;  	} -	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - -	/* Read entire ID string */ - -	for (i = 0; i < 8; i++) -		id_data[i] = chip->read_byte(mtd); -  	if (!type->name)  		return ERR_PTR(-ENODEV); @@ -2672,101 +2952,25 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,  	chip->chipsize = (uint64_t)type->chipsize << 20;  	if (!type->pagesize && chip->init_size) { -		/* set the pagesize, oobsize, erasesize by the driver*/ +		/* Set the pagesize, oobsize, erasesize by the driver */  		busw = chip->init_size(mtd, chip, id_data);  	} else if (!type->pagesize) { -		int extid; -		/* The 3rd id byte holds MLC / multichip data */ -		chip->cellinfo = id_data[2]; -		/* The 4th id byte is the important one */ -		extid = id_data[3]; - -		/* -		 * Field definitions are in the following datasheets: -		 * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32) -		 * New style   (6 byte ID): Samsung K9GBG08U0M (p.40) -		 * -		 * Check for wraparound + Samsung ID + nonzero 6th byte -		 * to decide what to do. -		 */ -		if (id_data[0] == id_data[6] && id_data[1] == id_data[7] && -				id_data[0] == NAND_MFR_SAMSUNG && -				(chip->cellinfo & NAND_CI_CELLTYPE_MSK) && -				id_data[5] != 0x00) { -			/* Calc pagesize */ -			mtd->writesize = 2048 << (extid & 0x03); -			extid >>= 2; -			/* Calc oobsize */ -			switch (extid & 0x03) { -			case 1: -				mtd->oobsize = 128; -				break; -			case 2: -				mtd->oobsize = 218; -				break; -			case 3: -				mtd->oobsize = 400; -				break; -			default: -				mtd->oobsize = 436; -				break; -			} -			extid >>= 2; -			/* Calc blocksize */ -			mtd->erasesize = (128 * 1024) << -				(((extid >> 1) & 0x04) | (extid & 0x03)); -			busw = 0; -		} else { -			/* Calc pagesize */ -			mtd->writesize = 1024 << (extid & 0x03); -			extid >>= 2; -			/* Calc oobsize */ -			mtd->oobsize = (8 << (extid & 0x01)) * -				(mtd->writesize >> 9); -			extid >>= 2; -			/* Calc blocksize. Blocksize is multiples of 64KiB */ -			mtd->erasesize = (64 * 1024) << (extid & 0x03); -			extid >>= 2; -			/* Get buswidth information */ -			busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; -		} +		/* Decode parameters from extended ID */ +		nand_decode_ext_id(mtd, chip, id_data, &busw);  	} else { -		/* -		 * Old devices have chip data hardcoded in the device id table -		 */ -		mtd->erasesize = type->erasesize; -		mtd->writesize = type->pagesize; -		mtd->oobsize = mtd->writesize / 32; -		busw = type->options & NAND_BUSWIDTH_16; - -		/* -		 * Check for Spansion/AMD ID + repeating 5th, 6th byte since -		 * some Spansion chips have erasesize that conflicts with size -		 * listed in nand_ids table -		 * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39) -		 */ -		if (*maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && -				id_data[5] == 0x00 && id_data[6] == 0x00 && -				id_data[7] == 0x00 && mtd->writesize == 512) { -			mtd->erasesize = 128 * 1024; -			mtd->erasesize <<= ((id_data[3] & 0x03) << 1); -		} +		nand_decode_id(mtd, chip, type, id_data, &busw);  	}  	/* Get chip options, preserve non chip based options */  	chip->options |= type->options; -	/* Check if chip is a not a samsung device. Do not clear the -	 * options for chips which are not having an extended id. +	/* +	 * Check if chip is not a Samsung device. Do not clear the +	 * options for chips which do not have an extended id.  	 */  	if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)  		chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;  ident_done: -	/* -	 * Set chip as a default. Board drivers can override it, if necessary -	 */ -	chip->options |= NAND_NO_AUTOINCR; -  	/* Try to identify manufacturer */  	for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {  		if (nand_manuf_ids[maf_idx].id == *maf_id) @@ -2775,21 +2979,23 @@ ident_done:  	/*  	 * Check, if buswidth is correct. Hardware drivers should set -	 * chip correct ! +	 * chip correct!  	 */  	if (busw != (chip->options & NAND_BUSWIDTH_16)) { -		printk(KERN_INFO "NAND device: Manufacturer ID:" -		       " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, -		       *dev_id, nand_manuf_ids[maf_idx].name, mtd->name); -		printk(KERN_WARNING "NAND bus width %d instead %d bit\n", -		       (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, -		       busw ? 16 : 8); +		pr_info("NAND device: Manufacturer ID:" +			" 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, +			*dev_id, nand_manuf_ids[maf_idx].name, mtd->name); +		pr_warn("NAND bus width %d instead %d bit\n", +			   (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, +			   busw ? 16 : 8);  		return ERR_PTR(-EINVAL);  	} +	nand_decode_bbm_options(mtd, chip, id_data); +  	/* Calculate the address shift from the page size */  	chip->page_shift = ffs(mtd->writesize) - 1; -	/* Convert chipsize to number of pages per chip -1. */ +	/* Convert chipsize to number of pages per chip -1 */  	chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;  	chip->bbt_erase_shift = chip->phys_erase_shift = @@ -2803,73 +3009,38 @@ ident_done:  	chip->badblockbits = 8; -	/* Set the bad block position */ -	if (mtd->writesize > 512 || (busw & NAND_BUSWIDTH_16)) -		chip->badblockpos = NAND_LARGE_BADBLOCK_POS; -	else -		chip->badblockpos = NAND_SMALL_BADBLOCK_POS; - -	/* -	 * Bad block marker is stored in the last page of each block -	 * on Samsung and Hynix MLC devices; stored in first two pages -	 * of each block on Micron devices with 2KiB pages and on -	 * SLC Samsung, Hynix, Toshiba and AMD/Spansion. All others scan -	 * only the first page. -	 */ -	if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) && -			(*maf_id == NAND_MFR_SAMSUNG || -			 *maf_id == NAND_MFR_HYNIX)) -		chip->options |= NAND_BBT_SCANLASTPAGE; -	else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) && -				(*maf_id == NAND_MFR_SAMSUNG || -				 *maf_id == NAND_MFR_HYNIX || -				 *maf_id == NAND_MFR_TOSHIBA || -				 *maf_id == NAND_MFR_AMD)) || -			(mtd->writesize == 2048 && -			 *maf_id == NAND_MFR_MICRON)) -		chip->options |= NAND_BBT_SCAN2NDPAGE; - -	/* -	 * Numonyx/ST 2K pages, x8 bus use BOTH byte 1 and 6 -	 */ -	if (!(busw & NAND_BUSWIDTH_16) && -			*maf_id == NAND_MFR_STMICRO && -			mtd->writesize == 2048) { -		chip->options |= NAND_BBT_SCANBYTE1AND6; -		chip->badblockpos = 0; -	} -  	/* Check for AND chips with 4 page planes */  	if (chip->options & NAND_4PAGE_ARRAY)  		chip->erase_cmd = multi_erase_cmd;  	else  		chip->erase_cmd = single_erase_cmd; -	/* Do not replace user supplied command function ! */ +	/* Do not replace user supplied command function! */  	if (mtd->writesize > 512 && chip->cmdfunc == nand_command)  		chip->cmdfunc = nand_command_lp; -	/* TODO onfi flash name */  	name = type->name;  #ifdef CONFIG_SYS_NAND_ONFI_DETECTION  	if (chip->onfi_version)  		name = chip->onfi_params.model;  #endif -	MTDDEBUG(MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:" -		 " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id, -		 nand_manuf_ids[maf_idx].name, name); +	pr_info("NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s)," +		" page size: %d, OOB size: %d\n", +		*maf_id, *dev_id, nand_manuf_ids[maf_idx].name, +		name, +		mtd->writesize, mtd->oobsize);  	return type;  }  /**   * nand_scan_ident - [NAND Interface] Scan for the NAND device - * @mtd:	     MTD device structure - * @maxchips:	     Number of chips to scan for - * @table:	     Alternative NAND ID table + * @mtd: MTD device structure + * @maxchips: number of chips to scan for + * @table: alternative NAND ID table   * - * This is the first phase of the normal nand_scan() function. It - * reads the flash ID and sets up MTD fields accordingly. + * This is the first phase of the normal nand_scan() function. It reads the + * flash ID and sets up MTD fields accordingly.   *   * The mtd->owner field must be set to the module of the caller.   */ @@ -2891,7 +3062,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,  	if (IS_ERR(type)) {  #ifndef CONFIG_SYS_NAND_QUIET_TEST -		printk(KERN_WARNING "No NAND device found!!!\n"); +		pr_warn("No NAND device found\n");  #endif  		chip->select_chip(mtd, -1);  		return PTR_ERR(type); @@ -2911,7 +3082,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,  	}  #ifdef DEBUG  	if (i > 1) -		printk(KERN_INFO "%d NAND chips detected\n", i); +		pr_info("%d NAND chips detected\n", i);  #endif  	/* Store the number of chips and calc total size for mtd */ @@ -2924,17 +3095,21 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,  /**   * nand_scan_tail - [NAND Interface] Scan for the NAND device - * @mtd:	    MTD device structure + * @mtd: MTD device structure   * - * This is the second phase of the normal nand_scan() function. It - * fills out all the uninitialized function pointers with the defaults - * and scans for a bad block table if appropriate. + * This is the second phase of the normal nand_scan() function. It fills out + * all the uninitialized function pointers with the defaults and scans for a + * bad block table if appropriate.   */  int nand_scan_tail(struct mtd_info *mtd)  {  	int i;  	struct nand_chip *chip = mtd->priv; +	/* New bad blocks should be marked in OOB, flash-based BBT, or both */ +	BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && +			!(chip->bbt_options & NAND_BBT_USE_FLASH)); +  	if (!(chip->options & NAND_OWN_BUFFERS))  		chip->buffers = memalign(ARCH_DMA_MINALIGN,  					 sizeof(*chip->buffers)); @@ -2945,7 +3120,7 @@ int nand_scan_tail(struct mtd_info *mtd)  	chip->oob_poi = chip->buffers->databuf + mtd->writesize;  	/* -	 * If no default placement scheme is given, select an appropriate one +	 * If no default placement scheme is given, select an appropriate one.  	 */  	if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) {  		switch (mtd->oobsize) { @@ -2962,16 +3137,22 @@ int nand_scan_tail(struct mtd_info *mtd)  			chip->ecc.layout = &nand_oob_128;  			break;  		default: -			printk(KERN_WARNING "No oob scheme defined for " -			       "oobsize %d\n", mtd->oobsize); +			pr_warn("No oob scheme defined for oobsize %d\n", +				   mtd->oobsize);  		}  	}  	if (!chip->write_page)  		chip->write_page = nand_write_page; +	/* set for ONFI nand */ +	if (!chip->onfi_set_features) +		chip->onfi_set_features = nand_onfi_set_features; +	if (!chip->onfi_get_features) +		chip->onfi_get_features = nand_onfi_get_features; +  	/* -	 * check ECC mode, default to software if 3byte/512byte hardware ECC is +	 * Check ECC mode, default to software if 3byte/512byte hardware ECC is  	 * selected and we have 256 byte pagesize fallback to software ECC  	 */ @@ -2980,15 +3161,15 @@ int nand_scan_tail(struct mtd_info *mtd)  		/* Similar to NAND_ECC_HW, but a separate read_page handle */  		if (!chip->ecc.calculate || !chip->ecc.correct ||  		     !chip->ecc.hwctl) { -			printk(KERN_WARNING "No ECC functions supplied; " -			       "Hardware ECC not possible\n"); +			pr_warn("No ECC functions supplied; " +				   "hardware ECC not possible\n");  			BUG();  		}  		if (!chip->ecc.read_page)  			chip->ecc.read_page = nand_read_page_hwecc_oob_first;  	case NAND_ECC_HW: -		/* Use standard hwecc read page function ? */ +		/* Use standard hwecc read page function? */  		if (!chip->ecc.read_page)  			chip->ecc.read_page = nand_read_page_hwecc;  		if (!chip->ecc.write_page) @@ -3009,11 +3190,11 @@ int nand_scan_tail(struct mtd_info *mtd)  		     chip->ecc.read_page == nand_read_page_hwecc ||  		     !chip->ecc.write_page ||  		     chip->ecc.write_page == nand_write_page_hwecc)) { -			printk(KERN_WARNING "No ECC functions supplied; " -			       "Hardware ECC not possible\n"); +			pr_warn("No ECC functions supplied; " +				   "hardware ECC not possible\n");  			BUG();  		} -		/* Use standard syndrome read/write page function ? */ +		/* Use standard syndrome read/write page function? */  		if (!chip->ecc.read_page)  			chip->ecc.read_page = nand_read_page_syndrome;  		if (!chip->ecc.write_page) @@ -3027,11 +3208,16 @@ int nand_scan_tail(struct mtd_info *mtd)  		if (!chip->ecc.write_oob)  			chip->ecc.write_oob = nand_write_oob_syndrome; -		if (mtd->writesize >= chip->ecc.size) +		if (mtd->writesize >= chip->ecc.size) { +			if (!chip->ecc.strength) { +				pr_warn("Driver must set ecc.strength when using hardware ECC\n"); +				BUG(); +			}  			break; -		printk(KERN_WARNING "%d byte HW ECC not possible on " -		       "%d byte page size, fallback to SW ECC\n", -		       chip->ecc.size, mtd->writesize); +		} +		pr_warn("%d byte HW ECC not possible on " +			   "%d byte page size, fallback to SW ECC\n", +			   chip->ecc.size, mtd->writesize);  		chip->ecc.mode = NAND_ECC_SOFT;  	case NAND_ECC_SOFT: @@ -3047,11 +3233,12 @@ int nand_scan_tail(struct mtd_info *mtd)  		if (!chip->ecc.size)  			chip->ecc.size = 256;  		chip->ecc.bytes = 3; +		chip->ecc.strength = 1;  		break;  	case NAND_ECC_SOFT_BCH:  		if (!mtd_nand_has_bch()) { -			printk(KERN_WARNING "CONFIG_MTD_ECC_BCH not enabled\n"); +			pr_warn("CONFIG_MTD_ECC_BCH not enabled\n");  			return -EINVAL;  		}  		chip->ecc.calculate = nand_bch_calculate_ecc; @@ -3066,8 +3253,8 @@ int nand_scan_tail(struct mtd_info *mtd)  		/*  		 * Board driver should supply ecc.size and ecc.bytes values to  		 * select how many bits are correctable; see nand_bch_init() -		 * for details. -		 * Otherwise, default to 4 bits for large page devices +		 * for details. Otherwise, default to 4 bits for large page +		 * devices.  		 */  		if (!chip->ecc.size && (mtd->oobsize >= 64)) {  			chip->ecc.size = 512; @@ -3078,13 +3265,14 @@ int nand_scan_tail(struct mtd_info *mtd)  					       chip->ecc.bytes,  					       &chip->ecc.layout);  		if (!chip->ecc.priv) -			printk(KERN_WARNING "BCH ECC initialization failed!\n"); - +			pr_warn("BCH ECC initialization failed!\n"); + 		chip->ecc.strength = +			chip->ecc.bytes * 8 / fls(8 * chip->ecc.size);  		break;  	case NAND_ECC_NONE: -		printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " -		       "This is not recommended !!\n"); +		pr_warn("NAND_ECC_NONE selected by board driver. " +		        "This is not recommended !!\n");  		chip->ecc.read_page = nand_read_page_raw;  		chip->ecc.write_page = nand_write_page_raw;  		chip->ecc.read_oob = nand_read_oob_std; @@ -3096,14 +3284,19 @@ int nand_scan_tail(struct mtd_info *mtd)  		break;  	default: -		printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n", -		       chip->ecc.mode); +		pr_warn("Invalid NAND_ECC_MODE %d\n", chip->ecc.mode);  		BUG();  	} +	/* For many systems, the standard OOB write also works for raw */ +	if (!chip->ecc.read_oob_raw) +		chip->ecc.read_oob_raw = chip->ecc.read_oob; +	if (!chip->ecc.write_oob_raw) +		chip->ecc.write_oob_raw = chip->ecc.write_oob; +  	/*  	 * The number of bytes available for a client to place data into -	 * the out of band area +	 * the out of band area.  	 */  	chip->ecc.layout->oobavail = 0;  	for (i = 0; chip->ecc.layout->oobfree[i].length @@ -3114,19 +3307,16 @@ int nand_scan_tail(struct mtd_info *mtd)  	/*  	 * Set the number of read / write steps for one page depending on ECC -	 * mode +	 * mode.  	 */  	chip->ecc.steps = mtd->writesize / chip->ecc.size;  	if (chip->ecc.steps * chip->ecc.size != mtd->writesize) { -		printk(KERN_WARNING "Invalid ecc parameters\n"); +		pr_warn("Invalid ECC parameters\n");  		BUG();  	}  	chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; -	/* -	 * Allow subpage writes up to ecc.steps. Not possible for MLC -	 * FLASH. -	 */ +	/* Allow subpage writes up to ecc.steps. Not possible for MLC flash */  	if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&  	    !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {  		switch (chip->ecc.steps) { @@ -3159,21 +3349,29 @@ int nand_scan_tail(struct mtd_info *mtd)  	mtd->type = MTD_NANDFLASH;  	mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :  						MTD_CAP_NANDFLASH; -	mtd->erase = nand_erase; -	mtd->point = NULL; -	mtd->unpoint = NULL; -	mtd->read = nand_read; -	mtd->write = nand_write; -	mtd->read_oob = nand_read_oob; -	mtd->write_oob = nand_write_oob; -	mtd->sync = nand_sync; -	mtd->lock = NULL; -	mtd->unlock = NULL; -	mtd->block_isbad = nand_block_isbad; -	mtd->block_markbad = nand_block_markbad; +	mtd->_erase = nand_erase; +	mtd->_point = NULL; +	mtd->_unpoint = NULL; +	mtd->_read = nand_read; +	mtd->_write = nand_write; +	mtd->_read_oob = nand_read_oob; +	mtd->_write_oob = nand_write_oob; +	mtd->_sync = nand_sync; +	mtd->_lock = NULL; +	mtd->_unlock = NULL; +	mtd->_block_isbad = nand_block_isbad; +	mtd->_block_markbad = nand_block_markbad; -	/* propagate ecc.layout to mtd_info */ +	/* propagate ecc info to mtd_info */  	mtd->ecclayout = chip->ecc.layout; +	mtd->ecc_strength = chip->ecc.strength; +	/* +	 * Initialize bitflip_threshold to its default prior scan_bbt() call. +	 * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be +	 * properly set. +	 */ +	if (!mtd->bitflip_threshold) +		mtd->bitflip_threshold = mtd->ecc_strength;  	/* Check, if we should skip the bad block table scan */  	if (chip->options & NAND_SKIP_BBTSCAN) @@ -3184,15 +3382,13 @@ int nand_scan_tail(struct mtd_info *mtd)  /**   * nand_scan - [NAND Interface] Scan for the NAND device - * @mtd:	MTD device structure - * @maxchips:	Number of chips to scan for - * - * This fills out all the uninitialized function pointers - * with the defaults. - * The flash ID is read and the mtd/chip structures are - * filled with the appropriate values. - * The mtd->owner field must be set to the module of the caller + * @mtd: MTD device structure + * @maxchips: number of chips to scan for   * + * This fills out all the uninitialized function pointers with the defaults. + * The flash ID is read and the mtd/chip structures are filled with the + * appropriate values. The mtd->owner field must be set to the module of the + * caller.   */  int nand_scan(struct mtd_info *mtd, int maxchips)  { @@ -3206,8 +3402,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips)  /**   * nand_release - [NAND Interface] Free resources held by the NAND device - * @mtd:	MTD device structure -*/ + * @mtd: MTD device structure + */  void nand_release(struct mtd_info *mtd)  {  	struct nand_chip *chip = mtd->priv; diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 74a7061b0..8ef58451d 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -4,7 +4,7 @@   *  Overview:   *   Bad block table support for the NAND driver   * - *  Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) + *  Copyright © 2004 Thomas Gleixner (tglx@linutronix.de)   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License version 2 as @@ -14,7 +14,7 @@   *   * When nand_scan_bbt is called, then it tries to find the bad block table   * depending on the options in the BBT descriptor(s). If no flash based BBT - * (NAND_USE_FLASH_BBT) is specified then the device is scanned for factory + * (NAND_BBT_USE_FLASH) is specified then the device is scanned for factory   * marked good / bad blocks. This information is used to create a memory BBT.   * Once a new bad block is discovered then the "factory" information is updated   * on the device. @@ -22,7 +22,7 @@   * BBT on flash. If a BBT is found then the contents are read and the memory   * based BBT is created. If a mirrored BBT is selected then the mirror is   * searched too and the versions are compared. If the mirror has a greater - * version number than the mirror BBT is used to build the memory based BBT. + * version number, then the mirror BBT is used to build the memory based BBT.   * If the tables are not versioned, then we "or" the bad block information.   * If one of the BBTs is out of date or does not exist it is (re)created.   * If no BBT exists at all then the device is scanned for factory marked @@ -36,9 +36,9 @@   * The table is marked in the OOB area with an ident pattern and a version   * number which indicates which of both tables is more up to date. If the NAND   * controller needs the complete OOB area for the ECC information then the - * option NAND_USE_FLASH_BBT_NO_OOB should be used: it moves the ident pattern - * and the version byte into the data area and the OOB area will remain - * untouched. + * option NAND_BBT_NO_OOB should be used (along with NAND_BBT_USE_FLASH, of + * course): it moves the ident pattern and the version byte into the data area + * and the OOB area will remain untouched.   *   * The table uses 2 bits per block   * 11b:		block is good @@ -63,126 +63,81 @@  #include <malloc.h>  #include <linux/compat.h>  #include <linux/mtd/mtd.h> +#include <linux/mtd/bbm.h>  #include <linux/mtd/nand.h>  #include <linux/mtd/nand_ecc.h>  #include <linux/bitops.h> +#include <linux/string.h>  #include <asm/errno.h>  static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)  { -	int ret; - -	ret = memcmp(buf, td->pattern, td->len); -	if (!ret) -		return ret; -	return -1; +	if (memcmp(buf, td->pattern, td->len)) +		return -1; +	return 0;  }  /**   * check_pattern - [GENERIC] check if a pattern is in the buffer - * @buf:	the buffer to search - * @len:	the length of buffer to search - * @paglen:	the pagelength - * @td:		search pattern descriptor - * - * Check for a pattern at the given place. Used to search bad block - * tables and good / bad block identifiers. - * If the SCAN_EMPTY option is set then check, if all bytes except the - * pattern area contain 0xff + * @buf: the buffer to search + * @len: the length of buffer to search + * @paglen: the pagelength + * @td: search pattern descriptor   * -*/ + * Check for a pattern at the given place. Used to search bad block tables and + * good / bad block identifiers. If the SCAN_EMPTY option is set then check, if + * all bytes except the pattern area contain 0xff. + */  static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)  { -	int i, end = 0; +	int end = 0;  	uint8_t *p = buf;  	if (td->options & NAND_BBT_NO_OOB)  		return check_pattern_no_oob(buf, td);  	end = paglen + td->offs; -	if (td->options & NAND_BBT_SCANEMPTY) { -		for (i = 0; i < end; i++) { -			if (p[i] != 0xff) -				return -1; -		} -	} +	if (td->options & NAND_BBT_SCANEMPTY) +		if (memchr_inv(p, 0xff, end)) +			return -1;  	p += end;  	/* Compare the pattern */ -	for (i = 0; i < td->len; i++) { -		if (p[i] != td->pattern[i]) -			return -1; -	} - -	/* Check both positions 1 and 6 for pattern? */ -	if (td->options & NAND_BBT_SCANBYTE1AND6) { -		if (td->options & NAND_BBT_SCANEMPTY) { -			p += td->len; -			end += NAND_SMALL_BADBLOCK_POS - td->offs; -			/* Check region between positions 1 and 6 */ -			for (i = 0; i < NAND_SMALL_BADBLOCK_POS - td->offs - td->len; -					i++) { -				if (*p++ != 0xff) -					return -1; -			} -		} -		else { -			p += NAND_SMALL_BADBLOCK_POS - td->offs; -		} -		/* Compare the pattern */ -		for (i = 0; i < td->len; i++) { -			if (p[i] != td->pattern[i]) -				return -1; -		} -	} +	if (memcmp(p, td->pattern, td->len)) +		return -1;  	if (td->options & NAND_BBT_SCANEMPTY) {  		p += td->len;  		end += td->len; -		for (i = end; i < len; i++) { -			if (*p++ != 0xff) -				return -1; -		} +		if (memchr_inv(p, 0xff, len - end)) +			return -1;  	}  	return 0;  }  /**   * check_short_pattern - [GENERIC] check if a pattern is in the buffer - * @buf:	the buffer to search - * @td:		search pattern descriptor + * @buf: the buffer to search + * @td:	search pattern descriptor   * - * Check for a pattern at the given place. Used to search bad block - * tables and good / bad block identifiers. Same as check_pattern, but - * no optional empty check - * -*/ + * Check for a pattern at the given place. Used to search bad block tables and + * good / bad block identifiers. Same as check_pattern, but no optional empty + * check. + */  static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)  { -	int i; -	uint8_t *p = buf; -  	/* Compare the pattern */ -	for (i = 0; i < td->len; i++) { -		if (p[td->offs + i] != td->pattern[i]) -			return -1; -	} -	/* Need to check location 1 AND 6? */ -	if (td->options & NAND_BBT_SCANBYTE1AND6) { -		for (i = 0; i < td->len; i++) { -			if (p[NAND_SMALL_BADBLOCK_POS + i] != td->pattern[i]) -				return -1; -		} -	} +	if (memcmp(buf + td->offs, td->pattern, td->len)) +		return -1;  	return 0;  }  /**   * add_marker_len - compute the length of the marker in data area - * @td:		BBT descriptor used for computation + * @td: BBT descriptor used for computation   * - * The length will be 0 if the markeris located in OOB area. + * The length will be 0 if the marker is located in OOB area.   */  static u32 add_marker_len(struct nand_bbt_descr *td)  { @@ -199,34 +154,33 @@ static u32 add_marker_len(struct nand_bbt_descr *td)  /**   * read_bbt - [GENERIC] Read the bad block table starting from page - * @mtd:	MTD device structure - * @buf:	temporary buffer - * @page:	the starting page - * @num:	the number of bbt descriptors to read - * @td:		the bbt describtion table - * @offs:	offset in the memory table + * @mtd: MTD device structure + * @buf: temporary buffer + * @page: the starting page + * @num: the number of bbt descriptors to read + * @td: the bbt describtion table + * @offs: offset in the memory table   *   * Read the bad block table starting from page. - *   */  static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,  		struct nand_bbt_descr *td, int offs)  { -	int res, i, j, act = 0; +	int res, ret = 0, i, j, act = 0;  	struct nand_chip *this = mtd->priv;  	size_t retlen, len, totlen;  	loff_t from;  	int bits = td->options & NAND_BBT_NRBITS_MSK; -	uint8_t msk = (uint8_t) ((1 << bits) - 1); +	uint8_t msk = (uint8_t)((1 << bits) - 1);  	u32 marker_len;  	int reserved_block_code = td->reserved_block_code;  	totlen = (num * bits) >> 3;  	marker_len = add_marker_len(td); -	from = ((loff_t) page) << this->page_shift; +	from = ((loff_t)page) << this->page_shift;  	while (totlen) { -		len = min(totlen, (size_t) (1 << this->bbt_erase_shift)); +		len = min(totlen, (size_t)(1 << this->bbt_erase_shift));  		if (marker_len) {  			/*  			 * In case the BBT marker is not in the OOB area it @@ -236,13 +190,20 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,  			from += marker_len;  			marker_len = 0;  		} -		res = mtd->read(mtd, from, len, &retlen, buf); +		res = mtd_read(mtd, from, len, &retlen, buf);  		if (res < 0) { -			if (retlen != len) { -				printk(KERN_INFO "nand_bbt: Error reading bad block table\n"); +			if (mtd_is_eccerr(res)) { +				pr_info("nand_bbt: ECC error in BBT at " +					"0x%012llx\n", from & ~mtd->writesize); +				return res; +			} else if (mtd_is_bitflip(res)) { +				pr_info("nand_bbt: corrected error in BBT at " +					"0x%012llx\n", from & ~mtd->writesize); +				ret = res; +			} else { +				pr_info("nand_bbt: error reading BBT\n");  				return res;  			} -			printk(KERN_WARNING "nand_bbt: ECC error while reading bad block table\n");  		}  		/* Analyse data */ @@ -253,17 +214,16 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,  				if (tmp == msk)  					continue;  				if (reserved_block_code && (tmp == reserved_block_code)) { -					printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%012llx\n", -					       (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); +					pr_info("nand_read_bbt: reserved block at 0x%012llx\n", +						 (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);  					this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);  					mtd->ecc_stats.bbtblocks++;  					continue;  				} -				MTDDEBUG(MTD_DEBUG_LEVEL0, "nand_read_bbt: " \ -					"Bad block at 0x%012llx\n", +				pr_info("nand_read_bbt: Bad block at 0x%012llx\n",  					(loff_t)((offs << 2) + (act >> 1))  					<< this->bbt_erase_shift); -				/* Factory marked bad or worn out ? */ +				/* Factory marked bad or worn out? */  				if (tmp == 0)  					this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);  				else @@ -274,20 +234,20 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,  		totlen -= len;  		from += len;  	} -	return 0; +	return ret;  }  /**   * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page - * @mtd:	MTD device structure - * @buf:	temporary buffer - * @td:		descriptor for the bad block table - * @chip:	read the table for a specific chip, -1 read all chips. - *		Applies only if NAND_BBT_PERCHIP option is set + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @chip: read the table for a specific chip, -1 read all chips; applies only if + *        NAND_BBT_PERCHIP option is set   * - * Read the bad block table for all chips starting at a given page - * We assume that the bbt bits are in consecutive order. -*/ + * Read the bad block table for all chips starting at a given page. We assume + * that the bbt bits are in consecutive order. + */  static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)  {  	struct nand_chip *this = mtd->priv; @@ -313,10 +273,8 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc  	return 0;  } -/* - * BBT marker is in the first page, no OOB. - */ -static int scan_read_raw_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs, +/* BBT marker is in the first page, no OOB */ +static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,  			 struct nand_bbt_descr *td)  {  	size_t retlen; @@ -326,70 +284,73 @@ static int scan_read_raw_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,  	if (td->options & NAND_BBT_VERSION)  		len++; -	return mtd->read(mtd, offs, len, &retlen, buf); +	return mtd_read(mtd, offs, len, &retlen, buf);  } -/* - * Scan read raw data from flash +/** + * scan_read_oob - [GENERIC] Scan data+OOB region to buffer + * @mtd: MTD device structure + * @buf: temporary buffer + * @offs: offset at which to scan + * @len: length of data region to read + * + * Scan read data from data+OOB. May traverse multiple pages, interleaving + * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest" + * ECC condition (error or bitflip). May quit on the first (non-ECC) error.   */ -static int scan_read_raw_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs, +static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,  			 size_t len)  {  	struct mtd_oob_ops ops; -	int res; +	int res, ret = 0; -	ops.mode = MTD_OOB_RAW; +	ops.mode = MTD_OPS_PLACE_OOB;  	ops.ooboffs = 0;  	ops.ooblen = mtd->oobsize; -  	while (len > 0) { -		if (len <= mtd->writesize) { -			ops.oobbuf = buf + len; -			ops.datbuf = buf; -			ops.len = len; -			return mtd->read_oob(mtd, offs, &ops); -		} else { -			ops.oobbuf = buf + mtd->writesize; -			ops.datbuf = buf; -			ops.len = mtd->writesize; -			res = mtd->read_oob(mtd, offs, &ops); +		ops.datbuf = buf; +		ops.len = min(len, (size_t)mtd->writesize); +		ops.oobbuf = buf + ops.len; -			if (res) +		res = mtd_read_oob(mtd, offs, &ops); +		if (res) { +			if (!mtd_is_bitflip_or_eccerr(res))  				return res; +			else if (mtd_is_eccerr(res) || !ret) +				ret = res;  		}  		buf += mtd->oobsize + mtd->writesize;  		len -= mtd->writesize; +		offs += mtd->writesize;  	} -	return 0; +	return ret;  } -static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, +static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs,  			 size_t len, struct nand_bbt_descr *td)  {  	if (td->options & NAND_BBT_NO_OOB) -		return scan_read_raw_data(mtd, buf, offs, td); +		return scan_read_data(mtd, buf, offs, td);  	else -		return scan_read_raw_oob(mtd, buf, offs, len); +		return scan_read_oob(mtd, buf, offs, len);  } -/* - * Scan write data with oob to flash - */ +/* Scan write data with oob to flash */  static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,  			  uint8_t *buf, uint8_t *oob)  {  	struct mtd_oob_ops ops; -	ops.mode = MTD_OOB_PLACE; +	ops.mode = MTD_OPS_PLACE_OOB;  	ops.ooboffs = 0;  	ops.ooblen = mtd->oobsize;  	ops.datbuf = buf;  	ops.oobbuf = oob;  	ops.len = len; -	return mtd->write_oob(mtd, offs, &ops); +	return mtd_write_oob(mtd, offs, &ops);  }  static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td) @@ -403,65 +364,60 @@ static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)  /**   * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page - * @mtd:	MTD device structure - * @buf:	temporary buffer - * @td:		descriptor for the bad block table - * @md:		descriptor for the bad block table mirror - * - * Read the bad block table(s) for all chips starting at a given page - * We assume that the bbt bits are in consecutive order. + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md:	descriptor for the bad block table mirror   * -*/ -static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, -			 struct nand_bbt_descr *td, struct nand_bbt_descr *md) + * Read the bad block table(s) for all chips starting at a given page. We + * assume that the bbt bits are in consecutive order. + */ +static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, +			  struct nand_bbt_descr *td, struct nand_bbt_descr *md)  {  	struct nand_chip *this = mtd->priv;  	/* Read the primary version, if available */  	if (td->options & NAND_BBT_VERSION) { -		scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift, +		scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift,  			      mtd->writesize, td);  		td->version[0] = buf[bbt_get_ver_offs(mtd, td)]; -		printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", -		       td->pages[0], td->version[0]); +		pr_info("Bad block table at page %d, version 0x%02X\n", +			 td->pages[0], td->version[0]);  	}  	/* Read the mirror version, if available */  	if (md && (md->options & NAND_BBT_VERSION)) { -		scan_read_raw(mtd, buf, (loff_t)md->pages[0] << this->page_shift, -			      mtd->writesize, td); +		scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift, +			      mtd->writesize, md);  		md->version[0] = buf[bbt_get_ver_offs(mtd, md)]; -		printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", -		       md->pages[0], md->version[0]); +		pr_info("Bad block table at page %d, version 0x%02X\n", +			 md->pages[0], md->version[0]);  	} -	return 1;  } -/* - * Scan a given block full - */ +/* Scan a given block full */  static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,  			   loff_t offs, uint8_t *buf, size_t readlen, -			   int scanlen, int len) +			   int scanlen, int numpages)  {  	int ret, j; -	ret = scan_read_raw_oob(mtd, buf, offs, readlen); -	if (ret) +	ret = scan_read_oob(mtd, buf, offs, readlen); +	/* Ignore ECC errors when checking for BBM */ +	if (ret && !mtd_is_bitflip_or_eccerr(ret))  		return ret; -	for (j = 0; j < len; j++, buf += scanlen) { +	for (j = 0; j < numpages; j++, buf += scanlen) {  		if (check_pattern(buf, scanlen, mtd->writesize, bd))  			return 1;  	}  	return 0;  } -/* - * Scan a given block partially - */ +/* Scan a given block partially */  static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, -			   loff_t offs, uint8_t *buf, int len) +			   loff_t offs, uint8_t *buf, int numpages)  {  	struct mtd_oob_ops ops;  	int j, ret; @@ -470,16 +426,16 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,  	ops.oobbuf = buf;  	ops.ooboffs = 0;  	ops.datbuf = NULL; -	ops.mode = MTD_OOB_PLACE; +	ops.mode = MTD_OPS_PLACE_OOB; -	for (j = 0; j < len; j++) { +	for (j = 0; j < numpages; j++) {  		/* -		 * Read the full oob until read_oob is fixed to -		 * handle single byte reads for 16 bit -		 * buswidth +		 * Read the full oob until read_oob is fixed to handle single +		 * byte reads for 16 bit buswidth.  		 */ -		ret = mtd->read_oob(mtd, offs, &ops); -		if (ret) +		ret = mtd_read_oob(mtd, offs, &ops); +		/* Ignore ECC errors when checking for BBM */ +		if (ret && !mtd_is_bitflip_or_eccerr(ret))  			return ret;  		if (check_short_pattern(buf, bd)) @@ -492,32 +448,32 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,  /**   * create_bbt - [GENERIC] Create a bad block table by scanning the device - * @mtd:	MTD device structure - * @buf:	temporary buffer - * @bd:		descriptor for the good/bad block search pattern - * @chip:	create the table for a specific chip, -1 read all chips. - *		Applies only if NAND_BBT_PERCHIP option is set + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * @chip: create the table for a specific chip, -1 read all chips; applies only + *        if NAND_BBT_PERCHIP option is set   * - * Create a bad block table by scanning the device - * for the given good/bad block identify pattern + * Create a bad block table by scanning the device for the given good/bad block + * identify pattern.   */  static int create_bbt(struct mtd_info *mtd, uint8_t *buf,  	struct nand_bbt_descr *bd, int chip)  {  	struct nand_chip *this = mtd->priv; -	int i, numblocks, len, scanlen; +	int i, numblocks, numpages, scanlen;  	int startblock;  	loff_t from;  	size_t readlen; -	MTDDEBUG(MTD_DEBUG_LEVEL0, "Scanning device for bad blocks\n"); +	pr_info("Scanning device for bad blocks\n");  	if (bd->options & NAND_BBT_SCANALLPAGES) -		len = 1 << (this->bbt_erase_shift - this->page_shift); +		numpages = 1 << (this->bbt_erase_shift - this->page_shift);  	else if (bd->options & NAND_BBT_SCAN2NDPAGE) -		len = 2; +		numpages = 2;  	else -		len = 1; +		numpages = 1;  	if (!(bd->options & NAND_BBT_SCANEMPTY)) {  		/* We need only read few bytes from the OOB area */ @@ -526,18 +482,20 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,  	} else {  		/* Full page content should be read */  		scanlen = mtd->writesize + mtd->oobsize; -		readlen = len * mtd->writesize; +		readlen = numpages * mtd->writesize;  	}  	if (chip == -1) { -		/* Note that numblocks is 2 * (real numblocks) here, see i+=2 -		 * below as it makes shifting and masking less painful */ +		/* +		 * Note that numblocks is 2 * (real numblocks) here, see i+=2 +		 * below as it makes shifting and masking less painful +		 */  		numblocks = mtd->size >> (this->bbt_erase_shift - 1);  		startblock = 0;  		from = 0;  	} else {  		if (chip >= this->numchips) { -			printk(KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", +			pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n",  			       chip + 1, this->numchips);  			return -EINVAL;  		} @@ -547,8 +505,8 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,  		from = (loff_t)startblock << (this->bbt_erase_shift - 1);  	} -	if (this->options & NAND_BBT_SCANLASTPAGE) -		from += mtd->erasesize - (mtd->writesize * len); +	if (this->bbt_options & NAND_BBT_SCANLASTPAGE) +		from += mtd->erasesize - (mtd->writesize * numpages);  	for (i = startblock; i < numblocks;) {  		int ret; @@ -557,17 +515,16 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,  		if (bd->options & NAND_BBT_SCANALLPAGES)  			ret = scan_block_full(mtd, bd, from, buf, readlen, -					      scanlen, len); +					      scanlen, numpages);  		else -			ret = scan_block_fast(mtd, bd, from, buf, len); +			ret = scan_block_fast(mtd, bd, from, buf, numpages);  		if (ret < 0)  			return ret;  		if (ret) {  			this->bbt[i >> 3] |= 0x03 << (i & 0x6); -			MTDDEBUG(MTD_DEBUG_LEVEL0, -				  "Bad eraseblock %d at 0x%012llx\n", +			pr_warn("Bad eraseblock %d at 0x%012llx\n",  				  i >> 1, (unsigned long long)from);  			mtd->ecc_stats.badblocks++;  		} @@ -580,20 +537,18 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,  /**   * search_bbt - [GENERIC] scan the device for a specific bad block table - * @mtd:	MTD device structure - * @buf:	temporary buffer - * @td:		descriptor for the bad block table + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table   * - * Read the bad block table by searching for a given ident pattern. - * Search is preformed either from the beginning up or from the end of - * the device downwards. The search starts always at the start of a - * block. - * If the option NAND_BBT_PERCHIP is given, each chip is searched - * for a bbt, which contains the bad block information of this chip. - * This is necessary to provide support for certain DOC devices. + * Read the bad block table by searching for a given ident pattern. Search is + * preformed either from the beginning up or from the end of the device + * downwards. The search starts always at the start of a block. If the option + * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains + * the bad block information of this chip. This is necessary to provide support + * for certain DOC devices.   * - * The bbt ident pattern resides in the oob area of the first page - * in a block. + * The bbt ident pattern resides in the oob area of the first page in a block.   */  static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)  { @@ -604,7 +559,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr  	int bbtblocks;  	int blocktopage = this->bbt_erase_shift - this->page_shift; -	/* Search direction top -> down ? */ +	/* Search direction top -> down? */  	if (td->options & NAND_BBT_LASTBLOCK) {  		startblock = (mtd->size >> this->bbt_erase_shift) - 1;  		dir = -1; @@ -613,7 +568,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr  		dir = 1;  	} -	/* Do we have a bbt per chip ? */ +	/* Do we have a bbt per chip? */  	if (td->options & NAND_BBT_PERCHIP) {  		chips = this->numchips;  		bbtblocks = this->chipsize >> this->bbt_erase_shift; @@ -634,7 +589,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr  			loff_t offs = (loff_t)actblock << this->bbt_erase_shift;  			/* Read first page */ -			scan_read_raw(mtd, buf, offs, mtd->writesize, td); +			scan_read(mtd, buf, offs, mtd->writesize, td);  			if (!check_pattern(buf, scanlen, mtd->writesize, td)) {  				td->pages[i] = actblock << blocktopage;  				if (td->options & NAND_BBT_VERSION) { @@ -649,10 +604,9 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr  	/* Check, if we found a bbt for each requested chip */  	for (i = 0; i < chips; i++) {  		if (td->pages[i] == -1) -			printk(KERN_WARNING "Bad block table not found for chip %d\n", i); +			pr_warn("Bad block table not found for chip %d\n", i);  		else -			MTDDEBUG(MTD_DEBUG_LEVEL0, "Bad block table found " \ -				"at page %d, version 0x%02X\n", td->pages[i], +			pr_info("Bad block table found at page %d, version 0x%02X\n", td->pages[i],  				td->version[i]);  	}  	return 0; @@ -660,14 +614,16 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr  /**   * search_read_bbts - [GENERIC] scan the device for bad block table(s) - * @mtd:	MTD device structure - * @buf:	temporary buffer - * @td:		descriptor for the bad block table - * @md:		descriptor for the bad block table mirror + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror   * - * Search and read the bad block table(s) -*/ -static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md) + * Search and read the bad block table(s). + */ +static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf, +			     struct nand_bbt_descr *td, +			     struct nand_bbt_descr *md)  {  	/* Search the primary table */  	search_bbt(mtd, buf, td); @@ -675,23 +631,18 @@ static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt  	/* Search the mirror table */  	if (md)  		search_bbt(mtd, buf, md); - -	/* Force result check */ -	return 1;  }  /**   * write_bbt - [GENERIC] (Re)write the bad block table + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * @chipsel: selector for a specific chip, -1 for all   * - * @mtd:	MTD device structure - * @buf:	temporary buffer - * @td:		descriptor for the bad block table - * @md:		descriptor for the bad block table mirror - * @chipsel:	selector for a specific chip, -1 for all - * - * (Re)write the bad block table - * -*/ + * (Re)write the bad block table. + */  static int write_bbt(struct mtd_info *mtd, uint8_t *buf,  		     struct nand_bbt_descr *td, struct nand_bbt_descr *md,  		     int chipsel) @@ -710,14 +661,14 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,  	ops.ooblen = mtd->oobsize;  	ops.ooboffs = 0;  	ops.datbuf = NULL; -	ops.mode = MTD_OOB_PLACE; +	ops.mode = MTD_OPS_PLACE_OOB;  	if (!rcode)  		rcode = 0xff; -	/* Write bad block table per chip rather than per device ? */ +	/* Write bad block table per chip rather than per device? */  	if (td->options & NAND_BBT_PERCHIP) {  		numblocks = (int)(this->chipsize >> this->bbt_erase_shift); -		/* Full device write or specific chip ? */ +		/* Full device write or specific chip? */  		if (chipsel == -1) {  			nrchips = this->numchips;  		} else { @@ -731,8 +682,8 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,  	/* Loop through the chips */  	for (; chip < nrchips; chip++) { - -		/* There was already a version of the table, reuse the page +		/* +		 * There was already a version of the table, reuse the page  		 * This applies for absolute placement too, as we have the  		 * page nr. in td->pages.  		 */ @@ -741,8 +692,10 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,  			goto write;  		} -		/* Automatic placement of the bad block table */ -		/* Search direction top -> down ? */ +		/* +		 * Automatic placement of the bad block table. Search direction +		 * top -> down? +		 */  		if (td->options & NAND_BBT_LASTBLOCK) {  			startblock = numblocks * (chip + 1) - 1;  			dir = -1; @@ -766,7 +719,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,  			if (!md || md->pages[chip] != page)  				goto write;  		} -		printk(KERN_ERR "No space left to write bad block table\n"); +		pr_err("No space left to write bad block table\n");  		return -ENOSPC;  	write: @@ -791,29 +744,27 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,  		bbtoffs = chip * (numblocks >> 2); -		to = ((loff_t) page) << this->page_shift; +		to = ((loff_t)page) << this->page_shift; -		/* Must we save the block contents ? */ +		/* Must we save the block contents? */  		if (td->options & NAND_BBT_SAVECONTENT) {  			/* Make it block aligned */ -			to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1)); +			to &= ~((loff_t)((1 << this->bbt_erase_shift) - 1));  			len = 1 << this->bbt_erase_shift; -			res = mtd->read(mtd, to, len, &retlen, buf); +			res = mtd_read(mtd, to, len, &retlen, buf);  			if (res < 0) {  				if (retlen != len) { -					printk(KERN_INFO "nand_bbt: Error " -					       "reading block for writing " -					       "the bad block table\n"); +					pr_info("nand_bbt: error reading block " +						"for writing the bad block table\n");  					return res;  				} -				printk(KERN_WARNING "nand_bbt: ECC error " -				       "while reading block for writing " -				       "bad block table\n"); +				pr_warn("nand_bbt: ECC error while reading " +					"block for writing bad block table\n");  			}  			/* Read oob data */  			ops.ooblen = (len >> this->page_shift) * mtd->oobsize;  			ops.oobbuf = &buf[len]; -			res = mtd->read_oob(mtd, to + mtd->writesize, &ops); +			res = mtd_read_oob(mtd, to + mtd->writesize, &ops);  			if (res < 0 || ops.oobretlen != ops.ooblen)  				goto outerr; @@ -821,19 +772,19 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,  			pageoffs = page - (int)(to >> this->page_shift);  			offs = pageoffs << this->page_shift;  			/* Preset the bbt area with 0xff */ -			memset(&buf[offs], 0xff, (size_t) (numblocks >> sft)); +			memset(&buf[offs], 0xff, (size_t)(numblocks >> sft));  			ooboffs = len + (pageoffs * mtd->oobsize);  		} else if (td->options & NAND_BBT_NO_OOB) {  			ooboffs = 0;  			offs = td->len; -			/* the version byte */ +			/* The version byte */  			if (td->options & NAND_BBT_VERSION)  				offs++;  			/* Calc length */ -			len = (size_t) (numblocks >> sft); +			len = (size_t)(numblocks >> sft);  			len += offs; -			/* Make it page aligned ! */ +			/* Make it page aligned! */  			len = ALIGN(len, mtd->writesize);  			/* Preset the buffer with 0xff */  			memset(buf, 0xff, len); @@ -841,8 +792,8 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,  			memcpy(buf, td->pattern, td->len);  		} else {  			/* Calc length */ -			len = (size_t) (numblocks >> sft); -			/* Make it page aligned ! */ +			len = (size_t)(numblocks >> sft); +			/* Make it page aligned! */  			len = ALIGN(len, mtd->writesize);  			/* Preset the buffer with 0xff */  			memset(buf, 0xff, len + @@ -856,13 +807,13 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,  		if (td->options & NAND_BBT_VERSION)  			buf[ooboffs + td->veroffs] = td->version[chip]; -		/* walk through the memory table */ +		/* Walk through the memory table */  		for (i = 0; i < numblocks;) {  			uint8_t dat;  			dat = this->bbt[bbtoffs + (i >> 2)];  			for (j = 0; j < 4; j++, i++) {  				int sftcnt = (i << (3 - sft)) & sftmsk; -				/* Do not store the reserved bbt blocks ! */ +				/* Do not store the reserved bbt blocks! */  				buf[offs + (i >> sft)] &=  					~(msk[dat & 0x03] << sftcnt);  				dat >>= 2; @@ -883,8 +834,8 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,  		if (res < 0)  			goto outerr; -		printk(KERN_DEBUG "Bad block table written to 0x%012llx, version " -		       "0x%02X\n", (unsigned long long)to, td->version[chip]); +		pr_info("Bad block table written to 0x%012llx, version 0x%02X\n", +			 (unsigned long long)to, td->version[chip]);  		/* Mark it as used */  		td->pages[chip] = page; @@ -892,19 +843,18 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,  	return 0;   outerr: -	printk(KERN_WARNING -	       "nand_bbt: Error while writing bad block table %d\n", res); +	pr_warn("nand_bbt: error while writing bad block table %d\n", res);  	return res;  }  /**   * nand_memory_bbt - [GENERIC] create a memory based bad block table - * @mtd:	MTD device structure - * @bd:		descriptor for the good/bad block search pattern + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern   * - * The function creates a memory based bbt by scanning the device - * for manufacturer / software marked good / bad blocks -*/ + * The function creates a memory based bbt by scanning the device for + * manufacturer / software marked good / bad blocks. + */  static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)  {  	struct nand_chip *this = mtd->priv; @@ -915,25 +865,24 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b  /**   * check_create - [GENERIC] create and write bbt(s) if necessary - * @mtd:	MTD device structure - * @buf:	temporary buffer - * @bd:		descriptor for the good/bad block search pattern + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern   * - * The function checks the results of the previous call to read_bbt - * and creates / updates the bbt(s) if necessary - * Creation is necessary if no bbt was found for the chip/device - * Update is necessary if one of the tables is missing or the - * version nr. of one table is less than the other -*/ + * The function checks the results of the previous call to read_bbt and creates + * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found + * for the chip/device. Update is necessary if one of the tables is missing or + * the version nr. of one table is less than the other. + */  static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)  { -	int i, chips, writeops, chipsel, res; +	int i, chips, writeops, create, chipsel, res, res2;  	struct nand_chip *this = mtd->priv;  	struct nand_bbt_descr *td = this->bbt_td;  	struct nand_bbt_descr *md = this->bbt_md;  	struct nand_bbt_descr *rd, *rd2; -	/* Do we have a bbt per chip ? */ +	/* Do we have a bbt per chip? */  	if (td->options & NAND_BBT_PERCHIP)  		chips = this->numchips;  	else @@ -941,86 +890,98 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc  	for (i = 0; i < chips; i++) {  		writeops = 0; +		create = 0;  		rd = NULL;  		rd2 = NULL; -		/* Per chip or per device ? */ +		res = res2 = 0; +		/* Per chip or per device? */  		chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; -		/* Mirrored table available ? */ +		/* Mirrored table available? */  		if (md) {  			if (td->pages[i] == -1 && md->pages[i] == -1) { +				create = 1;  				writeops = 0x03; -				goto create; -			} - -			if (td->pages[i] == -1) { +			} else if (td->pages[i] == -1) {  				rd = md; -				td->version[i] = md->version[i]; -				writeops = 1; -				goto writecheck; -			} - -			if (md->pages[i] == -1) { +				writeops = 0x01; +			} else if (md->pages[i] == -1) {  				rd = td; -				md->version[i] = td->version[i]; -				writeops = 2; -				goto writecheck; -			} - -			if (td->version[i] == md->version[i]) { +				writeops = 0x02; +			} else if (td->version[i] == md->version[i]) {  				rd = td;  				if (!(td->options & NAND_BBT_VERSION))  					rd2 = md; -				goto writecheck; -			} - -			if (((int8_t) (td->version[i] - md->version[i])) > 0) { +			} else if (((int8_t)(td->version[i] - md->version[i])) > 0) {  				rd = td; -				md->version[i] = td->version[i]; -				writeops = 2; +				writeops = 0x02;  			} else {  				rd = md; -				td->version[i] = md->version[i]; -				writeops = 1; +				writeops = 0x01;  			} - -			goto writecheck; -  		} else {  			if (td->pages[i] == -1) { +				create = 1;  				writeops = 0x01; -				goto create; +			} else { +				rd = td; +			} +		} + +		if (create) { +			/* Create the bad block table by scanning the device? */ +			if (!(td->options & NAND_BBT_CREATE)) +				continue; + +			/* Create the table in memory by scanning the chip(s) */ +			if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY)) +				create_bbt(mtd, buf, bd, chipsel); + +			td->version[i] = 1; +			if (md) +				md->version[i] = 1; +		} + +		/* Read back first? */ +		if (rd) { +			res = read_abs_bbt(mtd, buf, rd, chipsel); +			if (mtd_is_eccerr(res)) { +				/* Mark table as invalid */ +				rd->pages[i] = -1; +				rd->version[i] = 0; +				i--; +				continue; +			} +		} +		/* If they weren't versioned, read both */ +		if (rd2) { +			res2 = read_abs_bbt(mtd, buf, rd2, chipsel); +			if (mtd_is_eccerr(res2)) { +				/* Mark table as invalid */ +				rd2->pages[i] = -1; +				rd2->version[i] = 0; +				i--; +				continue;  			} -			rd = td; -			goto writecheck;  		} -	create: -		/* Create the bad block table by scanning the device ? */ -		if (!(td->options & NAND_BBT_CREATE)) -			continue; -		/* Create the table in memory by scanning the chip(s) */ -		if (!(this->options & NAND_CREATE_EMPTY_BBT)) -			create_bbt(mtd, buf, bd, chipsel); +		/* Scrub the flash table(s)? */ +		if (mtd_is_bitflip(res) || mtd_is_bitflip(res2)) +			writeops = 0x03; -		td->version[i] = 1; -		if (md) -			md->version[i] = 1; -	writecheck: -		/* read back first ? */ -		if (rd) -			read_abs_bbt(mtd, buf, rd, chipsel); -		/* If they weren't versioned, read both. */ -		if (rd2) -			read_abs_bbt(mtd, buf, rd2, chipsel); +		/* Update version numbers before writing */ +		if (md) { +			td->version[i] = max(td->version[i], md->version[i]); +			md->version[i] = td->version[i]; +		} -		/* Write the bad block table to the device ? */ +		/* Write the bad block table to the device? */  		if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {  			res = write_bbt(mtd, buf, td, md, chipsel);  			if (res < 0)  				return res;  		} -		/* Write the mirror bad block table to the device ? */ +		/* Write the mirror bad block table to the device? */  		if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {  			res = write_bbt(mtd, buf, md, td, chipsel);  			if (res < 0) @@ -1032,20 +993,19 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc  /**   * mark_bbt_regions - [GENERIC] mark the bad block table regions - * @mtd:	MTD device structure - * @td:		bad block table descriptor + * @mtd: MTD device structure + * @td: bad block table descriptor   * - * The bad block table regions are marked as "bad" to prevent - * accidental erasures / writes. The regions are identified by - * the mark 0x02. -*/ + * The bad block table regions are marked as "bad" to prevent accidental + * erasures / writes. The regions are identified by the mark 0x02. + */  static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)  {  	struct nand_chip *this = mtd->priv;  	int i, j, chips, block, nrblocks, update;  	uint8_t oldval, newval; -	/* Do we have a bbt per chip ? */ +	/* Do we have a bbt per chip? */  	if (td->options & NAND_BBT_PERCHIP) {  		chips = this->numchips;  		nrblocks = (int)(this->chipsize >> this->bbt_erase_shift); @@ -1082,9 +1042,11 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)  				update = 1;  			block += 2;  		} -		/* If we want reserved blocks to be recorded to flash, and some -		   new ones have been marked, then we need to update the stored -		   bbts.  This should only happen once. */ +		/* +		 * If we want reserved blocks to be recorded to flash, and some +		 * new ones have been marked, then we need to update the stored +		 * bbts.  This should only happen once. +		 */  		if (update && td->reserved_block_code)  			nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1));  	} @@ -1092,8 +1054,8 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)  /**   * verify_bbt_descr - verify the bad block description - * @mtd:	MTD device structure - * @bd:		the table to verify + * @mtd: MTD device structure + * @bd: the table to verify   *   * This functions performs a few sanity checks on the bad block description   * table. @@ -1111,16 +1073,16 @@ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)  	pattern_len = bd->len;  	bits = bd->options & NAND_BBT_NRBITS_MSK; -	BUG_ON((this->options & NAND_USE_FLASH_BBT_NO_OOB) && -			!(this->options & NAND_USE_FLASH_BBT)); +	BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) && +			!(this->bbt_options & NAND_BBT_USE_FLASH));  	BUG_ON(!bits);  	if (bd->options & NAND_BBT_VERSION)  		pattern_len++;  	if (bd->options & NAND_BBT_NO_OOB) { -		BUG_ON(!(this->options & NAND_USE_FLASH_BBT)); -		BUG_ON(!(this->options & NAND_USE_FLASH_BBT_NO_OOB)); +		BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH)); +		BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB));  		BUG_ON(bd->offs);  		if (bd->options & NAND_BBT_VERSION)  			BUG_ON(bd->veroffs != bd->len); @@ -1140,18 +1102,16 @@ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)  /**   * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) - * @mtd:	MTD device structure - * @bd:		descriptor for the good/bad block search pattern - * - * The function checks, if a bad block table(s) is/are already - * available. If not it scans the device for manufacturer - * marked good / bad blocks and writes the bad block table(s) to - * the selected place. + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern   * - * The bad block table memory is allocated here. It must be freed - * by calling the nand_free_bbt function. + * The function checks, if a bad block table(s) is/are already available. If + * not it scans the device for manufacturer marked good / bad blocks and writes + * the bad block table(s) to the selected place.   * -*/ + * The bad block table memory is allocated here. It must be freed by calling + * the nand_free_bbt function. + */  int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)  {  	struct nand_chip *this = mtd->priv; @@ -1161,19 +1121,21 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)  	struct nand_bbt_descr *md = this->bbt_md;  	len = mtd->size >> (this->bbt_erase_shift + 2); -	/* Allocate memory (2bit per block) and clear the memory bad block table */ +	/* +	 * Allocate memory (2bit per block) and clear the memory bad block +	 * table. +	 */  	this->bbt = kzalloc(len, GFP_KERNEL); -	if (!this->bbt) { -		printk(KERN_ERR "nand_scan_bbt: Out of memory\n"); +	if (!this->bbt)  		return -ENOMEM; -	} -	/* If no primary table decriptor is given, scan the device -	 * to build a memory based bad block table +	/* +	 * If no primary table decriptor is given, scan the device to build a +	 * memory based bad block table.  	 */  	if (!td) {  		if ((res = nand_memory_bbt(mtd, bd))) { -			printk(KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n"); +			pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");  			kfree(this->bbt);  			this->bbt = NULL;  		} @@ -1187,22 +1149,20 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)  	len += (len >> this->page_shift) * mtd->oobsize;  	buf = vmalloc(len);  	if (!buf) { -		printk(KERN_ERR "nand_bbt: Out of memory\n");  		kfree(this->bbt);  		this->bbt = NULL;  		return -ENOMEM;  	} -	/* Is the bbt at a given page ? */ +	/* Is the bbt at a given page? */  	if (td->options & NAND_BBT_ABSPAGE) { -		res = read_abs_bbts(mtd, buf, td, md); +		read_abs_bbts(mtd, buf, td, md);  	} else {  		/* Search the bad block table using a pattern in oob */ -		res = search_read_bbts(mtd, buf, td, md); +		search_read_bbts(mtd, buf, td, md);  	} -	if (res) -		res = check_create(mtd, buf, bd); +	res = check_create(mtd, buf, bd);  	/* Prevent the bbt regions from erasing / writing */  	mark_bbt_region(mtd, td); @@ -1215,15 +1175,15 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)  /**   * nand_update_bbt - [NAND Interface] update bad block table(s) - * @mtd:	MTD device structure - * @offs:	the offset of the newly marked block + * @mtd: MTD device structure + * @offs: the offset of the newly marked block   * - * The function updates the bad block table(s) -*/ + * The function updates the bad block table(s). + */  int nand_update_bbt(struct mtd_info *mtd, loff_t offs)  {  	struct nand_chip *this = mtd->priv; -	int len, res = 0, writeops = 0; +	int len, res = 0;  	int chip, chipsel;  	uint8_t *buf;  	struct nand_bbt_descr *td = this->bbt_td; @@ -1236,14 +1196,10 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)  	len = (1 << this->bbt_erase_shift);  	len += (len >> this->page_shift) * mtd->oobsize;  	buf = kmalloc(len, GFP_KERNEL); -	if (!buf) { -		printk(KERN_ERR "nand_update_bbt: Out of memory\n"); +	if (!buf)  		return -ENOMEM; -	} -	writeops = md != NULL ? 0x03 : 0x01; - -	/* Do we have a bbt per chip ? */ +	/* Do we have a bbt per chip? */  	if (td->options & NAND_BBT_PERCHIP) {  		chip = (int)(offs >> this->chip_shift);  		chipsel = chip; @@ -1256,14 +1212,14 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)  	if (md)  		md->version[chip]++; -	/* Write the bad block table to the device ? */ -	if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { +	/* Write the bad block table to the device? */ +	if (td->options & NAND_BBT_WRITE) {  		res = write_bbt(mtd, buf, td, md, chipsel);  		if (res < 0)  			goto out;  	} -	/* Write the mirror bad block table to the device ? */ -	if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { +	/* Write the mirror bad block table to the device? */ +	if (md && (md->options & NAND_BBT_WRITE)) {  		res = write_bbt(mtd, buf, md, td, chipsel);  	} @@ -1272,8 +1228,10 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)  	return res;  } -/* Define some generic bad / good block scan pattern which are used - * while scanning a device for factory marked good / bad blocks. */ +/* + * Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. + */  static uint8_t scan_ff_pattern[] = { 0xff, 0xff };  static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 }; @@ -1285,8 +1243,7 @@ static struct nand_bbt_descr agand_flashbased = {  	.pattern = scan_agand_pattern  }; -/* Generic flash bbt decriptors -*/ +/* Generic flash bbt descriptors */  static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };  static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; @@ -1296,7 +1253,7 @@ static struct nand_bbt_descr bbt_main_descr = {  	.offs =	8,  	.len = 4,  	.veroffs = 12, -	.maxblocks = 4, +	.maxblocks = NAND_BBT_SCAN_MAXBLOCKS,  	.pattern = bbt_pattern  }; @@ -1306,55 +1263,51 @@ static struct nand_bbt_descr bbt_mirror_descr = {  	.offs =	8,  	.len = 4,  	.veroffs = 12, -	.maxblocks = 4, +	.maxblocks = NAND_BBT_SCAN_MAXBLOCKS,  	.pattern = mirror_pattern  }; -static struct nand_bbt_descr bbt_main_no_bbt_descr = { +static struct nand_bbt_descr bbt_main_no_oob_descr = {  	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE  		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP  		| NAND_BBT_NO_OOB,  	.len = 4,  	.veroffs = 4, -	.maxblocks = 4, +	.maxblocks = NAND_BBT_SCAN_MAXBLOCKS,  	.pattern = bbt_pattern  }; -static struct nand_bbt_descr bbt_mirror_no_bbt_descr = { +static struct nand_bbt_descr bbt_mirror_no_oob_descr = {  	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE  		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP  		| NAND_BBT_NO_OOB,  	.len = 4,  	.veroffs = 4, -	.maxblocks = 4, +	.maxblocks = NAND_BBT_SCAN_MAXBLOCKS,  	.pattern = mirror_pattern  }; -#define BBT_SCAN_OPTIONS (NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE | \ -		NAND_BBT_SCANBYTE1AND6) +#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB)  /** - * nand_create_default_bbt_descr - [Internal] Creates a BBT descriptor structure - * @this:	NAND chip to create descriptor for + * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure + * @this: NAND chip to create descriptor for   *   * This function allocates and initializes a nand_bbt_descr for BBM detection - * based on the properties of "this". The new descriptor is stored in + * based on the properties of @this. The new descriptor is stored in   * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when   * passed to this function. - *   */ -static int nand_create_default_bbt_descr(struct nand_chip *this) +static int nand_create_badblock_pattern(struct nand_chip *this)  {  	struct nand_bbt_descr *bd;  	if (this->badblock_pattern) { -		printk(KERN_WARNING "BBT descr already allocated; not replacing.\n"); +		pr_warn("Bad block pattern already allocated; not replacing\n");  		return -EINVAL;  	}  	bd = kzalloc(sizeof(*bd), GFP_KERNEL); -	if (!bd) { -		printk(KERN_ERR "nand_create_default_bbt_descr: Out of memory\n"); +	if (!bd)  		return -ENOMEM; -	} -	bd->options = this->options & BBT_SCAN_OPTIONS; +	bd->options = this->bbt_options & BADBLOCK_SCAN_MASK;  	bd->offs = this->badblockpos;  	bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1;  	bd->pattern = scan_ff_pattern; @@ -1365,22 +1318,20 @@ static int nand_create_default_bbt_descr(struct nand_chip *this)  /**   * nand_default_bbt - [NAND Interface] Select a default bad block table for the device - * @mtd:	MTD device structure - * - * This function selects the default bad block table - * support for the device and calls the nand_scan_bbt function + * @mtd: MTD device structure   * -*/ + * This function selects the default bad block table support for the device and + * calls the nand_scan_bbt function. + */  int nand_default_bbt(struct mtd_info *mtd)  {  	struct nand_chip *this = mtd->priv; -	/* Default for AG-AND. We must use a flash based -	 * bad block table as the devices have factory marked -	 * _good_ blocks. Erasing those blocks leads to loss -	 * of the good / bad information, so we _must_ store -	 * this information in a good / bad table during -	 * startup +	/* +	 * Default for AG-AND. We must use a flash based bad block table as the +	 * devices have factory marked _good_ blocks. Erasing those blocks +	 * leads to loss of the good / bad information, so we _must_ store this +	 * information in a good / bad table during startup.  	 */  	if (this->options & NAND_IS_AND) {  		/* Use the default pattern descriptors */ @@ -1388,17 +1339,17 @@ int nand_default_bbt(struct mtd_info *mtd)  			this->bbt_td = &bbt_main_descr;  			this->bbt_md = &bbt_mirror_descr;  		} -		this->options |= NAND_USE_FLASH_BBT; +		this->bbt_options |= NAND_BBT_USE_FLASH;  		return nand_scan_bbt(mtd, &agand_flashbased);  	} -	/* Is a flash based bad block table requested ? */ -	if (this->options & NAND_USE_FLASH_BBT) { +	/* Is a flash based bad block table requested? */ +	if (this->bbt_options & NAND_BBT_USE_FLASH) {  		/* Use the default pattern descriptors */  		if (!this->bbt_td) { -			if (this->options & NAND_USE_FLASH_BBT_NO_OOB) { -				this->bbt_td = &bbt_main_no_bbt_descr; -				this->bbt_md = &bbt_mirror_no_bbt_descr; +			if (this->bbt_options & NAND_BBT_NO_OOB) { +				this->bbt_td = &bbt_main_no_oob_descr; +				this->bbt_md = &bbt_mirror_no_oob_descr;  			} else {  				this->bbt_td = &bbt_main_descr;  				this->bbt_md = &bbt_mirror_descr; @@ -1410,18 +1361,17 @@ int nand_default_bbt(struct mtd_info *mtd)  	}  	if (!this->badblock_pattern) -		nand_create_default_bbt_descr(this); +		nand_create_badblock_pattern(this);  	return nand_scan_bbt(mtd, this->badblock_pattern);  }  /**   * nand_isbad_bbt - [NAND Interface] Check if a block is bad - * @mtd:	MTD device structure - * @offs:	offset in the device - * @allowbbt:	allow access to bad block table region - * -*/ + * @mtd: MTD device structure + * @offs: offset in the device + * @allowbbt: allow access to bad block table region + */  int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)  {  	struct nand_chip *this = mtd->priv; diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 39535497f..f856778b5 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -71,14 +71,15 @@ const struct nand_flash_dev nand_flash_ids[] = {  	 * These are the new chips with large page size. The pagesize and the  	 * erasesize is determined from the extended id bytes  	 */ -#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR) +#define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS  #define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) -	/*512 Megabit */ +	/* 512 Megabit */  	{"NAND 64MiB 1,8V 8-bit",	0xA2, 0,  64, 0, LP_OPTIONS},  	{"NAND 64MiB 1,8V 8-bit",	0xA0, 0,  64, 0, LP_OPTIONS},  	{"NAND 64MiB 3,3V 8-bit",	0xF2, 0,  64, 0, LP_OPTIONS},  	{"NAND 64MiB 3,3V 8-bit",	0xD0, 0,  64, 0, LP_OPTIONS}, +	{"NAND 64MiB 3,3V 8-bit",	0xF0, 0,  64, 0, LP_OPTIONS},  	{"NAND 64MiB 1,8V 16-bit",	0xB2, 0,  64, 0, LP_OPTIONS16},  	{"NAND 64MiB 1,8V 16-bit",	0xB0, 0,  64, 0, LP_OPTIONS16},  	{"NAND 64MiB 3,3V 16-bit",	0xC2, 0,  64, 0, LP_OPTIONS16}, @@ -157,9 +158,7 @@ const struct nand_flash_dev nand_flash_ids[] = {  	 * writes possible, but not implemented now  	 */  	{"AND 128MiB 3,3V 8-bit",	0x01, 2048, 128, 0x4000, -	 NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY | -	 BBT_AUTO_REFRESH -	}, +	 NAND_IS_AND | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH},  	{NULL,}  }; @@ -176,6 +175,9 @@ const struct nand_manufacturers nand_manuf_ids[] = {  	{NAND_MFR_STMICRO, "ST Micro"},  	{NAND_MFR_HYNIX, "Hynix"},  	{NAND_MFR_MICRON, "Micron"}, -	{NAND_MFR_AMD, "AMD"}, +	{NAND_MFR_AMD, "AMD/Spansion"}, +	{NAND_MFR_MACRONIX, "Macronix"}, +	{NAND_MFR_EON, "Eon"},  	{0x0, "Unknown"}  }; + diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 4727f9c98..d81972ca2 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -121,7 +121,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)  		WATCHDOG_RESET();  		if (!opts->scrub && bbtest) { -			int ret = meminfo->block_isbad(meminfo, erase.addr); +			int ret = mtd_block_isbad(meminfo, erase.addr);  			if (ret > 0) {  				if (!opts->quiet)  					printf("\rSkipping bad block at  " @@ -144,7 +144,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)  		erased_length++; -		result = meminfo->erase(meminfo, &erase); +		result = mtd_erase(meminfo, &erase);  		if (result != 0) {  			printf("\n%s: MTD Erase failure: %d\n",  			       mtd_device, result); @@ -153,15 +153,16 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)  		/* format for JFFS2 ? */  		if (opts->jffs2 && chip->ecc.layout->oobavail >= 8) { -			chip->ops.ooblen = 8; -			chip->ops.datbuf = NULL; -			chip->ops.oobbuf = (uint8_t *)&cleanmarker; -			chip->ops.ooboffs = 0; -			chip->ops.mode = MTD_OOB_AUTO; +			struct mtd_oob_ops ops; +			ops.ooblen = 8; +			ops.datbuf = NULL; +			ops.oobbuf = (uint8_t *)&cleanmarker; +			ops.ooboffs = 0; +			ops.mode = MTD_OPS_AUTO_OOB; -			result = meminfo->write_oob(meminfo, +			result = mtd_write_oob(meminfo,  			                            erase.addr, -			                            &chip->ops); +			                            &ops);  			if (result != 0) {  				printf("\n%s: MTD writeoob failure: %d\n",  				       mtd_device, result); @@ -458,7 +459,8 @@ static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length,  static size_t drop_ffs(const nand_info_t *nand, const u_char *buf,  			const size_t *len)  { -	size_t i, l = *len; +	size_t l = *len; +	ssize_t i;  	for (i = l - 1; i >= 0; i--)  		if (buf[i] != 0xFF) @@ -604,7 +606,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,  			ops.len = pagesize;  			ops.ooblen = nand->oobsize; -			ops.mode = MTD_OOB_AUTO; +			ops.mode = MTD_OPS_AUTO_OOB;  			ops.ooboffs = 0;  			pages = write_size / pagesize_oob; @@ -614,7 +616,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,  				ops.datbuf = p_buffer;  				ops.oobbuf = ops.datbuf + pagesize; -				rval = nand->write_oob(nand, offset, &ops); +				rval = mtd_write_oob(nand, offset, &ops);  				if (rval != 0)  					break; diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 213d2c945..94b90332d 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -216,6 +216,7 @@ int board_nand_init(struct nand_chip *nand)  	nand->ecc.mode = NAND_ECC_HW;  	nand->ecc.size = 256;  	nand->ecc.bytes = 3; +	nand->ecc.strength = 1;  	nand->select_chip = ndfc_select_chip;  #ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT diff --git a/drivers/mtd/nand/nomadik.c b/drivers/mtd/nand/nomadik.c index b76f4cbb5..dc8e51373 100644 --- a/drivers/mtd/nand/nomadik.c +++ b/drivers/mtd/nand/nomadik.c @@ -212,6 +212,7 @@ int board_nand_init(struct nand_chip *chip)  	chip->ecc.mode = NAND_ECC_HW;  	chip->ecc.bytes = 3;  	chip->ecc.size = 512; +	chip->ecc.strength = 1;  	chip->ecc.layout = &nomadik_ecc_layout;  	chip->ecc.calculate = nomadik_ecc_calculate;  	chip->ecc.hwctl = nomadik_ecc_hwctl; diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index bc1bcad3b..5d088227e 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -590,11 +590,12 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat,   * @mtd:	mtd info structure   * @chip:	nand chip info structure   * @buf:	buffer to store read data + * @oob_required: caller expects OOB data read to chip->oob_poi   * @page:	page number to read   *   */  static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, -				uint8_t *buf, int page) +				uint8_t *buf, int oob_required, int page)  {  	int i, eccsize = chip->ecc.size;  	int eccbytes = chip->ecc.bytes; @@ -804,6 +805,7 @@ void omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength)  	nand->ecc.hwctl = NULL;  	nand->ecc.correct = NULL;  	nand->ecc.calculate = NULL; +	nand->ecc.strength = eccstrength;  	/* Setup the ecc configurations again */  	if (hardware) { @@ -901,7 +903,7 @@ int board_nand_init(struct nand_chip *nand)  	nand->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd;  	nand->cmd_ctrl = omap_nand_hwcontrol; -	nand->options = NAND_NO_PADDING | NAND_CACHEPRG | NAND_NO_AUTOINCR; +	nand->options = NAND_NO_PADDING | NAND_CACHEPRG;  	/* If we are 16 bit dev, our gpmc config tells us that */  	if ((readl(&gpmc_cfg->cs[cs].config1) & 0x3000) == 0x1000)  		nand->options |= NAND_BUSWIDTH_16; @@ -934,6 +936,7 @@ int board_nand_init(struct nand_chip *nand)  	nand->ecc.layout = &hw_bch8_nand_oob;  	nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;  	nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; +	nand->ecc.strength = 8;  	nand->ecc.hwctl = omap_enable_ecc_bch;  	nand->ecc.correct = omap_correct_data_bch;  	nand->ecc.calculate = omap_calculate_ecc_bch; @@ -952,6 +955,7 @@ int board_nand_init(struct nand_chip *nand)  	nand->ecc.hwctl = omap_enable_hwecc;  	nand->ecc.correct = omap_correct_data;  	nand->ecc.calculate = omap_calculate_ecc; +	nand->ecc.strength = 1;  	omap_hwecc_init(nand);  #endif  #endif diff --git a/drivers/mtd/nand/s3c2410_nand.c b/drivers/mtd/nand/s3c2410_nand.c index e1a459b00..43d8213e0 100644 --- a/drivers/mtd/nand/s3c2410_nand.c +++ b/drivers/mtd/nand/s3c2410_nand.c @@ -173,6 +173,7 @@ int board_nand_init(struct nand_chip *nand)  	nand->ecc.mode = NAND_ECC_HW;  	nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;  	nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; +	nand->ecc.strength = 1;  #else  	nand->ecc.mode = NAND_ECC_SOFT;  #endif diff --git a/drivers/mtd/nand/tegra_nand.c b/drivers/mtd/nand/tegra_nand.c index 4d94cc6f5..6afbec61e 100644 --- a/drivers/mtd/nand/tegra_nand.c +++ b/drivers/mtd/nand/tegra_nand.c @@ -707,7 +707,7 @@ static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,   *		-EIO when command timeout   */  static int nand_read_page_hwecc(struct mtd_info *mtd, -	struct nand_chip *chip, uint8_t *buf, int page) +	struct nand_chip *chip, uint8_t *buf, int oob_required, int page)  {  	return nand_rw_page(mtd, chip, buf, page, 1, 0);  } @@ -719,8 +719,8 @@ static int nand_read_page_hwecc(struct mtd_info *mtd,   * @param chip	nand chip info structure   * @param buf	data buffer   */ -static void nand_write_page_hwecc(struct mtd_info *mtd, -	struct nand_chip *chip, const uint8_t *buf) +static int nand_write_page_hwecc(struct mtd_info *mtd, +	struct nand_chip *chip, const uint8_t *buf, int oob_required)  {  	int page;  	struct nand_drv *info; @@ -731,6 +731,7 @@ static void nand_write_page_hwecc(struct mtd_info *mtd,  		(readl(&info->reg->addr_reg2) << 16);  	nand_rw_page(mtd, chip, (uint8_t *)buf, page, 1, 1); +	return 0;  } @@ -746,7 +747,7 @@ static void nand_write_page_hwecc(struct mtd_info *mtd,   *		-EIO when command timeout   */  static int nand_read_page_raw(struct mtd_info *mtd, -	struct nand_chip *chip, uint8_t *buf, int page) +	struct nand_chip *chip, uint8_t *buf, int oob_required, int page)  {  	return nand_rw_page(mtd, chip, buf, page, 0, 0);  } @@ -758,8 +759,8 @@ static int nand_read_page_raw(struct mtd_info *mtd,   * @param chip	nand chip info structure   * @param buf	data buffer   */ -static void nand_write_page_raw(struct mtd_info *mtd, -		struct nand_chip *chip,	const uint8_t *buf) +static int nand_write_page_raw(struct mtd_info *mtd, +		struct nand_chip *chip,	const uint8_t *buf, int oob_required)  {  	int page;  	struct nand_drv *info; @@ -769,6 +770,7 @@ static void nand_write_page_raw(struct mtd_info *mtd,  		(readl(&info->reg->addr_reg2) << 16);  	nand_rw_page(mtd, chip, (uint8_t *)buf, page, 0, 1); +	return 0;  }  /** @@ -873,19 +875,13 @@ static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip,   * @param mtd		mtd info structure   * @param chip		nand chip info structure   * @param page		page number to read - * @param sndcmd	flag whether to issue read command or not - * @return	1 - issue read command next time - *		0 - not to issue   */  static int nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, -	int page, int sndcmd) +	int page)  { -	if (sndcmd) { -		chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); -		sndcmd = 0; -	} +	chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);  	nand_rw_oob(mtd, chip, page, 0, 0); -	return sndcmd; +	return 0;  }  /** @@ -1018,6 +1014,7 @@ int tegra_nand_init(struct nand_chip *nand, int devnum)  	nand->ecc.write_page_raw = nand_write_page_raw;  	nand->ecc.read_oob = nand_read_oob;  	nand->ecc.write_oob = nand_write_oob; +	nand->ecc.strength = 1;  	nand->select_chip = nand_select_chip;  	nand->dev_ready  = nand_dev_ready;  	nand->priv = &nand_ctrl; diff --git a/drivers/mtd/nand/tegra_nand.h b/drivers/mtd/nand/tegra_nand.h index 7e74be75f..622b86980 100644 --- a/drivers/mtd/nand/tegra_nand.h +++ b/drivers/mtd/nand/tegra_nand.h @@ -224,7 +224,7 @@ enum {  #define BCH_DEC_STATUS_MAX_CORR_CNT_MASK	(0x1f << 8)  #define BCH_DEC_STATUS_PAGE_NUMBER_MASK		0xFF -#define LP_OPTIONS (NAND_NO_READRDY | NAND_NO_AUTOINCR) +#define LP_OPTIONS	0  struct nand_ctlr {  	u32	command;	/* offset 00h */ diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 858e32274..ddfe7e7c7 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -743,7 +743,7 @@ static void onenand_release_device(struct mtd_info *mtd)  }  /** - * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer + * onenand_transfer_auto_oob - [INTERN] oob auto-placement transfer   * @param mtd		MTD device structure   * @param buf		destination address   * @param column	oob offset to read from @@ -807,7 +807,7 @@ static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status)  		return status;  	/* check if we failed due to uncorrectable error */ -	if (status != -EBADMSG && status != ONENAND_BBT_READ_ECC_ERROR) +	if (!mtd_is_eccerr(status) && status != ONENAND_BBT_READ_ECC_ERROR)  		return status;  	/* check if address lies in MLC region */ @@ -847,7 +847,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,  	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); -	if (ops->mode == MTD_OOB_AUTO) +	if (ops->mode == MTD_OPS_AUTO_OOB)  		oobsize = this->ecclayout->oobavail;  	else  		oobsize = mtd->oobsize; @@ -914,7 +914,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,  			thisooblen = oobsize - oobcolumn;  			thisooblen = min_t(int, thisooblen, ooblen - oobread); -			if (ops->mode == MTD_OOB_AUTO) +			if (ops->mode == MTD_OPS_AUTO_OOB)  				onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);  			else  				this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen); @@ -929,7 +929,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,  			if (unlikely(ret))  				ret = onenand_recover_lsb(mtd, from, ret);  			onenand_update_bufferram(mtd, from, !ret); -			if (ret == -EBADMSG) +			if (mtd_is_eccerr(ret))  				ret = 0;  		} @@ -950,7 +950,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,  			/* Now wait for load */  			ret = this->wait(mtd, FL_READING);  			onenand_update_bufferram(mtd, from, !ret); -			if (ret == -EBADMSG) +			if (mtd_is_eccerr(ret))  				ret = 0;  		}  	} @@ -987,7 +987,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,  	struct mtd_ecc_stats stats;  	int read = 0, thislen, column, oobsize;  	size_t len = ops->ooblen; -	mtd_oob_mode_t mode = ops->mode; +	unsigned int mode = ops->mode;  	u_char *buf = ops->oobbuf;  	int ret = 0, readcmd; @@ -998,7 +998,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,  	/* Initialize return length value */  	ops->oobretlen = 0; -	if (mode == MTD_OOB_AUTO) +	if (mode == MTD_OPS_AUTO_OOB)  		oobsize = this->ecclayout->oobavail;  	else  		oobsize = mtd->oobsize; @@ -1041,7 +1041,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,  			break;  		} -		if (mode == MTD_OOB_AUTO) +		if (mode == MTD_OPS_AUTO_OOB)  			onenand_transfer_auto_oob(mtd, buf, column, thislen);  		else  			this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen); @@ -1115,10 +1115,10 @@ int onenand_read_oob(struct mtd_info *mtd, loff_t from,  	int ret;  	switch (ops->mode) { -	case MTD_OOB_PLACE: -	case MTD_OOB_AUTO: +	case MTD_OPS_PLACE_OOB: +	case MTD_OPS_AUTO_OOB:  		break; -	case MTD_OOB_RAW: +	case MTD_OPS_RAW:  		/* Not implemented yet */  	default:  		return -EINVAL; @@ -1337,7 +1337,7 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr,  #define NOTALIGNED(x)	((x & (this->subpagesize - 1)) != 0)  /** - * onenand_fill_auto_oob - [Internal] oob auto-placement transfer + * onenand_fill_auto_oob - [INTERN] oob auto-placement transfer   * @param mtd           MTD device structure   * @param oob_buf       oob buffer   * @param buf           source address @@ -1404,19 +1404,13 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,  	ops->retlen = 0;  	ops->oobretlen = 0; -	/* Do not allow writes past end of device */ -	if (unlikely((to + len) > mtd->size)) { -		printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n"); -		return -EINVAL; -	} -  	/* Reject writes, which are not page aligned */  	if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {  		printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n");  		return -EINVAL;  	} -	if (ops->mode == MTD_OOB_AUTO) +	if (ops->mode == MTD_OPS_AUTO_OOB)  		oobsize = this->ecclayout->oobavail;  	else  		oobsize = mtd->oobsize; @@ -1450,7 +1444,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,  			/* We send data to spare ram with oobsize  			 *                          * to prevent byte access */  			memset(oobbuf, 0xff, mtd->oobsize); -			if (ops->mode == MTD_OOB_AUTO) +			if (ops->mode == MTD_OPS_AUTO_OOB)  				onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);  			else  				memcpy(oobbuf + oobcolumn, oob, thisooblen); @@ -1502,7 +1496,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,  }  /** - * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band + * onenand_write_oob_nolock - [INTERN] OneNAND write out-of-band   * @param mtd           MTD device structure   * @param to            offset to write to   * @param len           number of bytes to write @@ -1521,7 +1515,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,  	u_char *oobbuf;  	size_t len = ops->ooblen;  	const u_char *buf = ops->oobbuf; -	mtd_oob_mode_t mode = ops->mode; +	unsigned int mode = ops->mode;  	to += ops->ooboffs; @@ -1530,7 +1524,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,  	/* Initialize retlen, in case of early exit */  	ops->oobretlen = 0; -	if (mode == MTD_OOB_AUTO) +	if (mode == MTD_OPS_AUTO_OOB)  		oobsize = this->ecclayout->oobavail;  	else  		oobsize = mtd->oobsize; @@ -1571,7 +1565,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,  		/* We send data to spare ram with oobsize  		 * to prevent byte access */  		memset(oobbuf, 0xff, mtd->oobsize); -		if (mode == MTD_OOB_AUTO) +		if (mode == MTD_OPS_AUTO_OOB)  			onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen);  		else  			memcpy(oobbuf + column, buf, thislen); @@ -1661,10 +1655,10 @@ int onenand_write_oob(struct mtd_info *mtd, loff_t to,  	int ret;  	switch (ops->mode) { -	case MTD_OOB_PLACE: -	case MTD_OOB_AUTO: +	case MTD_OPS_PLACE_OOB: +	case MTD_OPS_AUTO_OOB:  		break; -	case MTD_OOB_RAW: +	case MTD_OPS_RAW:  		/* Not implemented yet */  	default:  		return -EINVAL; @@ -1720,13 +1714,6 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)  	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n",  			(unsigned int) addr, len); -	/* Do not allow erase past end of device */ -	if (unlikely((len + addr) > mtd->size)) { -		MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:" -					"Erase past end of device\n"); -		return -EINVAL; -	} -  	if (FLEXONENAND(this)) {  		/* Find the eraseregion of this address */  		i = flexonenand_region(mtd, addr); @@ -1762,8 +1749,6 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)  		return -EINVAL;  	} -	instr->fail_addr = 0xffffffff; -  	/* Grab the lock and see if the device is available */  	onenand_get_device(mtd, FL_ERASING); @@ -1889,7 +1874,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)  	struct bbm_info *bbm = this->bbm;  	u_char buf[2] = {0, 0};  	struct mtd_oob_ops ops = { -		.mode = MTD_OOB_PLACE, +		.mode = MTD_OPS_PLACE_OOB,  		.ooblen = 2,  		.oobbuf = buf,  		.ooboffs = 0, @@ -1915,7 +1900,6 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)   */  int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)  { -	struct onenand_chip *this = mtd->priv;  	int ret;  	ret = onenand_block_isbad(mtd, ofs); @@ -1926,7 +1910,7 @@ int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)  		return ret;  	} -	ret = this->block_markbad(mtd, ofs); +	ret = mtd_block_markbad(mtd, ofs);  	return ret;  } @@ -2386,7 +2370,7 @@ static int flexonenand_check_blocks_erased(struct mtd_info *mtd,  	int i, ret;  	int block;  	struct mtd_oob_ops ops = { -		.mode = MTD_OOB_PLACE, +		.mode = MTD_OPS_PLACE_OOB,  		.ooboffs = 0,  		.ooblen	= mtd->oobsize,  		.datbuf	= NULL, @@ -2645,14 +2629,14 @@ int onenand_probe(struct mtd_info *mtd)  		mtd->size = this->chipsize;  	mtd->flags = MTD_CAP_NANDFLASH; -	mtd->erase = onenand_erase; -	mtd->read = onenand_read; -	mtd->write = onenand_write; -	mtd->read_oob = onenand_read_oob; -	mtd->write_oob = onenand_write_oob; -	mtd->sync = onenand_sync; -	mtd->block_isbad = onenand_block_isbad; -	mtd->block_markbad = onenand_block_markbad; +	mtd->_erase = onenand_erase; +	mtd->_read = onenand_read; +	mtd->_write = onenand_write; +	mtd->_read_oob = onenand_read_oob; +	mtd->_write_oob = onenand_write_oob; +	mtd->_sync = onenand_sync; +	mtd->_block_isbad = onenand_block_isbad; +	mtd->_block_markbad = onenand_block_markbad;  	return 0;  } diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 9d5da5470..0267c2c5c 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -87,7 +87,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t * buf,  	startblock = 0;  	from = 0; -	ops.mode = MTD_OOB_PLACE; +	ops.mode = MTD_OPS_PLACE_OOB;  	ops.ooblen = readlen;  	ops.oobbuf = buf;  	ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; @@ -200,10 +200,8 @@ int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)  	len = this->chipsize >> (this->erase_shift + 2);  	/* Allocate memory (2bit per block) */  	bbm->bbt = malloc(len); -	if (!bbm->bbt) { -		printk(KERN_ERR "onenand_scan_bbt: Out of memory\n"); +	if (!bbm->bbt)  		return -ENOMEM; -	}  	/* Clear the memory bad block table */  	memset(bbm->bbt, 0x00, len); diff --git a/drivers/mtd/spi/spansion.c b/drivers/mtd/spi/spansion.c index dad30b54c..b3ef90f13 100644 --- a/drivers/mtd/spi/spansion.c +++ b/drivers/mtd/spi/spansion.c @@ -90,6 +90,13 @@ static const struct spansion_spi_flash_params spansion_spi_flash_table[] = {  		.name = "S25FL032P",  	},  	{ +		.idcode1 = 0x0216, +		.idcode2 = 0x4d00, +		.pages_per_sector = 256, +		.nr_sectors = 128, +		.name = "S25FL064P", +	}, +	{  		.idcode1 = 0x2018,  		.idcode2 = 0x4d01,  		.pages_per_sector = 256, @@ -101,7 +108,7 @@ static const struct spansion_spi_flash_params spansion_spi_flash_table[] = {  		.idcode2 = 0x4d01,  		.pages_per_sector = 256,  		.nr_sectors = 512, -		.name = "S25FL256S", +		.name = "S25FL256S_64K",  	},  }; diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 6507aa34b..9991d47a4 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -124,9 +124,6 @@ int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset,  		}  	} -	debug("SF: program %s %zu bytes @ %#x\n", -	      ret ? "failure" : "success", len, offset); -  	spi_release_bus(flash->spi);  	return ret;  } @@ -150,8 +147,10 @@ int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,  	u8 cmd[5];  	/* Handle memory-mapped SPI */ -	if (flash->memory_map) +	if (flash->memory_map) {  		memcpy(data, flash->memory_map + offset, len); +		return 0; +	}  	cmd[0] = CMD_READ_ARRAY_FAST;  	spi_flash_addr(offset, cmd); @@ -205,7 +204,7 @@ int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout)  int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len)  { -	u32 start, end, erase_size; +	u32 end, erase_size;  	int ret;  	u8 cmd[4]; @@ -225,8 +224,7 @@ int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len)  		cmd[0] = CMD_ERASE_4K;  	else  		cmd[0] = CMD_ERASE_64K; -	start = offset; -	end = start + len; +	end = offset + len;  	while (offset < end) {  		spi_flash_addr(offset, cmd); @@ -248,8 +246,6 @@ int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len)  			goto out;  	} -	debug("SF: Successfully erased %zu bytes @ %#x\n", len, start); -   out:  	spi_release_bus(flash->spi);  	return ret; diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c index 27162091c..845780849 100644 --- a/drivers/mtd/spi/winbond.c +++ b/drivers/mtd/spi/winbond.c @@ -18,6 +18,21 @@ struct winbond_spi_flash_params {  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", @@ -63,13 +78,18 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {  		.name			= "W25Q128",  	},  	{ +		.id			= 0x4019, +		.nr_blocks		= 512, +		.name			= "W25Q256", +	}, +	{  		.id			= 0x5014, -		.nr_blocks		= 128, -		.name			= "W25Q80", +		.nr_blocks		= 16, +		.name			= "W25Q80BW",  	},  	{  		.id			= 0x6016, -		.nr_blocks		= 512, +		.nr_blocks		= 64,  		.name			= "W25Q32DW",  	},  	{ @@ -104,7 +124,7 @@ struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode)  	}  	flash->page_size = 256; -	flash->sector_size = 4096; +	flash->sector_size = (idcode[1] == 0x20) ? 65536 : 4096;  	flash->size = 4096 * 16 * params->nr_blocks;  	return flash; diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index a708162e4..25888220d 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -539,7 +539,7 @@ static int io_init(struct ubi_device *ubi)  	ubi->peb_count  = mtd_div_by_eb(ubi->mtd->size, ubi->mtd);  	ubi->flash_size = ubi->mtd->size; -	if (ubi->mtd->block_isbad && ubi->mtd->block_markbad) +	if (mtd_can_have_bb(ubi->mtd))  		ubi->bad_allowed = 1;  	ubi->min_io_size = ubi->mtd->writesize; diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index d523c94b1..d2d3c9c58 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -460,7 +460,7 @@ retry:  		if (err == UBI_IO_BITFLIPS) {  			scrub = 1;  			err = 0; -		} else if (err == -EBADMSG) { +		} else if (mtd_is_eccerr(err)) {  			if (vol->vol_type == UBI_DYNAMIC_VOLUME)  				goto out_unlock;  			scrub = 1; diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 842389400..05de9aeb6 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -154,7 +154,7 @@ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,  	addr = (loff_t)pnum * ubi->peb_size + offset;  retry: -	err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf); +	err = mtd_read(ubi->mtd, addr, len, &read, buf);  	if (err) {  		if (err == -EUCLEAN) {  			/* @@ -268,7 +268,7 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,  	}  	addr = (loff_t)pnum * ubi->peb_size + offset; -	err = ubi->mtd->write(ubi->mtd, addr, len, &written, buf); +	err = mtd_write(ubi->mtd, addr, len, &written, buf);  	if (err) {  		ubi_err("error %d while writing %d bytes to PEB %d:%d, written"  			" %zd bytes", err, len, pnum, offset, written); @@ -318,7 +318,7 @@ retry:  	ei.callback = erase_callback;  	ei.priv     = (unsigned long)&wq; -	err = ubi->mtd->erase(ubi->mtd, &ei); +	err = mtd_erase(ubi->mtd, &ei);  	if (err) {  		if (retries++ < UBI_IO_RETRIES) {  			dbg_io("error %d while erasing PEB %d, retry", @@ -516,7 +516,7 @@ int ubi_io_is_bad(const struct ubi_device *ubi, int pnum)  	if (ubi->bad_allowed) {  		int ret; -		ret = mtd->block_isbad(mtd, (loff_t)pnum * ubi->peb_size); +		ret = mtd_block_isbad(mtd, (loff_t)pnum * ubi->peb_size);  		if (ret < 0)  			ubi_err("error %d while checking if PEB %d is bad",  				ret, pnum); @@ -551,7 +551,7 @@ int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum)  	if (!ubi->bad_allowed)  		return 0; -	err = mtd->block_markbad(mtd, (loff_t)pnum * ubi->peb_size); +	err = mtd_block_markbad(mtd, (loff_t)pnum * ubi->peb_size);  	if (err)  		ubi_err("cannot mark PEB %d bad, error %d", pnum, err);  	return err; @@ -1242,7 +1242,7 @@ static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset,  	loff_t addr = (loff_t)pnum * ubi->peb_size + offset;  	mutex_lock(&ubi->dbg_buf_mutex); -	err = ubi->mtd->read(ubi->mtd, addr, len, &read, ubi->dbg_peb_buf); +	err = mtd_read(ubi->mtd, addr, len, &read, ubi->dbg_peb_buf);  	if (err && err != -EUCLEAN) {  		ubi_err("error %d while reading %d bytes from PEB %d:%d, "  			"read %zd bytes", err, len, pnum, offset, read); diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 423d47915..e55318879 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -349,7 +349,7 @@ int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,  		return 0;  	err = ubi_eba_read_leb(ubi, vol, lnum, buf, offset, len, check); -	if (err && err == -EBADMSG && vol->vol_type == UBI_STATIC_VOLUME) { +	if (err && mtd_is_eccerr(err) && vol->vol_type == UBI_STATIC_VOLUME) {  		ubi_warn("mark volume %d as corrupted", vol_id);  		vol->corrupted = 1;  	} diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c index a6410bfb6..e8660d997 100644 --- a/drivers/mtd/ubi/misc.c +++ b/drivers/mtd/ubi/misc.c @@ -82,7 +82,7 @@ int ubi_check_volume(struct ubi_device *ubi, int vol_id)  		err = ubi_eba_read_leb(ubi, vol, i, buf, 0, size, 1);  		if (err) { -			if (err == -EBADMSG) +			if (mtd_is_eccerr(err))  				err = 1;  			break;  		} diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index f679f0649..29d232001 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -388,7 +388,7 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi,  		err = ubi_io_read_data(ubi, leb[seb->lnum], seb->pnum, 0,  				       ubi->vtbl_size); -		if (err == UBI_IO_BITFLIPS || err == -EBADMSG) +		if (err == UBI_IO_BITFLIPS || mtd_is_eccerr(err))  			/*  			 * Scrub the PEB later. Note, -EBADMSG indicates an  			 * uncorrectable ECC error, but we have our own CRC and diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 1ae35d360..14999447b 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -26,7 +26,8 @@ include $(TOPDIR)/config.mk  LIB	:= $(obj)libpci.o  COBJS-$(CONFIG_FSL_PCI_INIT) += fsl_pci_init.o -COBJS-$(CONFIG_PCI) += pci.o pci_auto.o pci_indirect.o +COBJS-$(CONFIG_PCI) += pci.o pci_auto.o +COBJS-$(CONFIG_PCI_INDIRECT_BRIDGE) += pci_indirect.o  COBJS-$(CONFIG_FTPCI100) += pci_ftpci100.o  COBJS-$(CONFIG_IXP_PCI) += pci_ixp.o  COBJS-$(CONFIG_SH4_PCI) += pci_sh4.o diff --git a/drivers/pci/fsl_pci_init.c b/drivers/pci/fsl_pci_init.c index 77ac1f7c7..621c89912 100644 --- a/drivers/pci/fsl_pci_init.c +++ b/drivers/pci/fsl_pci_init.c @@ -211,7 +211,7 @@ static int fsl_pci_setup_inbound_windows(struct pci_controller *hose,  	return 1;  } -#ifdef CONFIG_SYS_FSL_SRIO_PCIE_BOOT_MASTER +#ifdef CONFIG_SRIO_PCIE_BOOT_MASTER  static void fsl_pcie_boot_master(pit_t *pi)  {  	/* configure inbound window for slave's u-boot image */ @@ -388,7 +388,7 @@ void fsl_pci_init(struct pci_controller *hose, struct fsl_pci_info *pci_info)  	/* see if we are a PCIe or PCI controller */  	pci_hose_read_config_byte(hose, dev, FSL_PCIE_CAP_ID, &pcie_cap); -#ifdef CONFIG_SYS_FSL_SRIO_PCIE_BOOT_MASTER +#ifdef CONFIG_SRIO_PCIE_BOOT_MASTER  	/* boot from PCIE --master */  	char *s = getenv("bootmaster");  	char pcie[6]; @@ -624,7 +624,7 @@ int fsl_pci_init_port(struct fsl_pci_info *pci_info,  	if (fsl_is_pci_agent(hose)) {  		fsl_pci_config_unlock(hose);  		hose->last_busno = hose->first_busno; -#ifdef CONFIG_SYS_FSL_SRIO_PCIE_BOOT_MASTER +#ifdef CONFIG_SRIO_PCIE_BOOT_MASTER  	} else {  		/* boot from PCIE --master releases slave's core 0 */  		char *s = getenv("bootmaster"); diff --git a/drivers/spi/armada100_spi.c b/drivers/spi/armada100_spi.c index afdbe0508..b237c7c0f 100644 --- a/drivers/spi/armada100_spi.c +++ b/drivers/spi/armada100_spi.c @@ -182,15 +182,8 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,  		goto done;  	} -	if (dout) -		pss->tx = dout; -	else -		pss->tx = NULL; - -	if (din) -		pss->rx = din; -	else -		pss->rx = NULL; +	pss->tx = dout; +	pss->rx = din;  	if (flags & SPI_XFER_BEGIN) {  		spi_cs_activate(slave); diff --git a/drivers/spi/exynos_spi.c b/drivers/spi/exynos_spi.c index 607e1cdec..01378d098 100644 --- a/drivers/spi/exynos_spi.c +++ b/drivers/spi/exynos_spi.c @@ -51,6 +51,7 @@ struct exynos_spi_slave {  	unsigned int mode;  	enum periph_id periph_id;	/* Peripheral ID for this device */  	unsigned int fifo_size; +	int skip_preamble;  };  static struct spi_bus *spi_get_bus(unsigned dev_index) @@ -105,6 +106,8 @@ struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs,  	else  		spi_slave->fifo_size = 256; +	spi_slave->skip_preamble = 0; +  	spi_slave->freq = bus->frequency;  	if (max_hz)  		spi_slave->freq = min(max_hz, spi_slave->freq); @@ -217,17 +220,23 @@ static void spi_request_bytes(struct exynos_spi *regs, int count)  	writel(count | SPI_PACKET_CNT_EN, ®s->pkt_cnt);  } -static void spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo, -			void **dinp, void const **doutp) +static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo, +			void **dinp, void const **doutp, unsigned long flags)  {  	struct exynos_spi *regs = spi_slave->regs;  	uchar *rxp = *dinp;  	const uchar *txp = *doutp;  	int rx_lvl, tx_lvl;  	uint out_bytes, in_bytes; +	int toread; +	unsigned start = get_timer(0); +	int stopping;  	out_bytes = in_bytes = todo; +	stopping = spi_slave->skip_preamble && (flags & SPI_XFER_END) && +					!(spi_slave->mode & SPI_SLAVE); +  	/*  	 * If there's something to send, do a software reset and set a  	 * transaction size. @@ -238,6 +247,8 @@ static void spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,  	 * Bytes are transmitted/received in pairs. Wait to receive all the  	 * data because then transmission will be done as well.  	 */ +	toread = in_bytes; +  	while (in_bytes) {  		int temp; @@ -248,15 +259,43 @@ static void spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,  			writel(temp, ®s->tx_data);  			out_bytes--;  		} -		if (rx_lvl > 0 && in_bytes) { +		if (rx_lvl > 0) {  			temp = readl(®s->rx_data); -			if (rxp) -				*rxp++ = temp; -			in_bytes--; +			if (spi_slave->skip_preamble) { +				if (temp == SPI_PREAMBLE_END_BYTE) { +					spi_slave->skip_preamble = 0; +					stopping = 0; +				} +			} else { +				if (rxp || stopping) +					*rxp++ = temp; +				in_bytes--; +			} +			toread--; +		} else if (!toread) { +			/* +			 * We have run out of input data, but haven't read +			 * enough bytes after the preamble yet. Read some more, +			 * and make sure that we transmit dummy bytes too, to +			 * keep things going. +			 */ +			assert(!out_bytes); +			out_bytes = in_bytes; +			toread = in_bytes; +			txp = NULL; +			spi_request_bytes(regs, toread); +		} +		if (spi_slave->skip_preamble && get_timer(start) > 100) { +			printf("SPI timeout: in_bytes=%d, out_bytes=%d, ", +			       in_bytes, out_bytes); +			return -1;  		}  	} +  	*dinp = rxp;  	*doutp = txp; + +	return 0;  }  /** @@ -276,6 +315,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,  	struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);  	int upto, todo;  	int bytelen; +	int ret = 0;  	/* spi core configured to do 8 bit transfers */  	if (bitlen % 8) { @@ -289,16 +329,24 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,  	/* Exynos SPI limits each transfer to 65535 bytes */  	bytelen =  bitlen / 8; -	for (upto = 0; upto < bytelen; upto += todo) { +	for (upto = 0; !ret && upto < bytelen; upto += todo) {  		todo = min(bytelen - upto, (1 << 16) - 1); -		spi_rx_tx(spi_slave, todo, &din, &dout); +		ret = spi_rx_tx(spi_slave, todo, &din, &dout, flags); +		if (ret) +			break;  	}  	/* Stop the transaction, if necessary. */ -	if ((flags & SPI_XFER_END)) +	if ((flags & SPI_XFER_END) && !(spi_slave->mode & SPI_SLAVE)) {  		spi_cs_deactivate(slave); +		if (spi_slave->skip_preamble) { +			assert(!spi_slave->skip_preamble); +			debug("Failed to complete premable transaction\n"); +			ret = -1; +		} +	} -	return 0; +	return ret;  }  /** @@ -325,6 +373,7 @@ void spi_cs_activate(struct spi_slave *slave)  	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;  }  /** diff --git a/drivers/spi/tegra114_spi.c b/drivers/spi/tegra114_spi.c index b11a0a1ff..4d2af483d 100644 --- a/drivers/spi/tegra114_spi.c +++ b/drivers/spi/tegra114_spi.c @@ -152,13 +152,11 @@ struct spi_slave *tegra114_spi_setup_slave(unsigned int bus, unsigned int cs,  		return NULL;  	} -	spi = malloc(sizeof(struct tegra_spi_slave)); +	spi = spi_alloc_slave(struct tegra_spi_slave, bus, cs);  	if (!spi) {  		printf("SPI error: malloc of SPI structure failed\n");  		return NULL;  	} -	spi->slave.bus = bus; -	spi->slave.cs = cs;  	spi->ctrl = &spi_ctrls[bus];  	if (!spi->ctrl) {  		printf("SPI error: could not find controller for bus %d\n", diff --git a/drivers/spi/tegra20_sflash.c b/drivers/spi/tegra20_sflash.c index 9322ce7f6..7c3a3fc35 100644 --- a/drivers/spi/tegra20_sflash.c +++ b/drivers/spi/tegra20_sflash.c @@ -132,8 +132,6 @@ struct spi_slave *tegra20_spi_setup_slave(unsigned int bus, unsigned int cs,  		printf("SPI error: malloc of SPI structure failed\n");  		return NULL;  	} -	spi->slave.bus = bus; -	spi->slave.cs = cs;  	spi->ctrl = &spi_ctrls[bus];  	if (!spi->ctrl) {  		printf("SPI error: could not find controller for bus %d\n", diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile index e8c159c0f..913dd9c86 100644 --- a/drivers/tpm/Makefile +++ b/drivers/tpm/Makefile @@ -25,9 +25,10 @@ LIB := $(obj)libtpm.o  $(shell mkdir -p $(obj)slb9635_i2c) -COBJS-$(CONFIG_GENERIC_LPC_TPM) = generic_lpc_tpm.o -COBJS-$(CONFIG_INFINEON_TPM_I2C) += tis_i2c.o slb9635_i2c/tpm.o -COBJS-$(CONFIG_INFINEON_TPM_I2C) += slb9635_i2c/tpm_tis_i2c.o +# TODO: Merge tpm_tis_lpc.c with tpm.c +COBJS-$(CONFIG_TPM_TIS_I2C) += tpm.o +COBJS-$(CONFIG_TPM_TIS_I2C) += tpm_tis_i2c.o +COBJS-$(CONFIG_TPM_TIS_LPC) += tpm_tis_lpc.o  COBJS	:= $(COBJS-y)  SRCS	:= $(COBJS:.o=.c) diff --git a/drivers/tpm/slb9635_i2c/compatibility.h b/drivers/tpm/slb9635_i2c/compatibility.h deleted file mode 100644 index 62dc9fa96..000000000 --- a/drivers/tpm/slb9635_i2c/compatibility.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2011 Infineon Technologies - * - * Authors: - * Peter Huewe <huewe.external@infineon.com> - * - * Version: 2.1.1 - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - */ - -#ifndef _COMPATIBILITY_H_ -#define _COMPATIBILITY_H_ - -/* all includes from U-Boot */ -#include <linux/types.h> -#include <linux/unaligned/be_byteshift.h> -#include <asm-generic/errno.h> -#include <compiler.h> -#include <common.h> - -/* extended error numbers from linux (see errno.h) */ -#define	ECANCELED	125	/* Operation Canceled */ - -#define msleep(t) udelay((t)*1000) - -/* Timer frequency. Corresponds to msec timer resolution*/ -#define HZ             1000 - -#define dev_dbg(dev, format, arg...) debug(format, ##arg) -#define dev_err(dev, format, arg...) printf(format, ##arg) -#define dev_info(dev, format, arg...) debug(format, ##arg) -#define dbg_printf debug - -#endif diff --git a/drivers/tpm/tis_i2c.c b/drivers/tpm/tis_i2c.c index e818fbaf5..22554e145 100644 --- a/drivers/tpm/tis_i2c.c +++ b/drivers/tpm/tis_i2c.c @@ -68,6 +68,10 @@ static int tpm_decode_config(struct tpm *dev)  	node = fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9635_TPM);  	if (node < 0) { +		node = fdtdec_next_compatible(blob, 0, +					      COMPAT_INFINEON_SLB9645_TPM); +	} +	if (node < 0) {  		debug("%s: Node not found\n", __func__);  		return -1;  	} diff --git a/drivers/tpm/slb9635_i2c/tpm.c b/drivers/tpm/tpm.c index 496c48e8c..b65733419 100644 --- a/drivers/tpm/slb9635_i2c/tpm.c +++ b/drivers/tpm/tpm.c @@ -32,11 +32,30 @@   * MA 02111-1307 USA   */ -#include <malloc.h> -#include "tpm.h" +#include <config.h> +#include <common.h> +#include <compiler.h> +#include <fdtdec.h> +#include <i2c.h> +#include <tpm.h> +#include <asm-generic/errno.h> +#include <linux/types.h> +#include <linux/unaligned/be_byteshift.h> -/* global structure for tpm chip data */ -struct tpm_chip g_chip; +#include "tpm_private.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* TPM configuration */ +struct tpm { +	int i2c_bus; +	int slave_addr; +	char inited; +	int old_bus; +} tpm; + +/* Global structure for tpm chip data */ +static struct tpm_chip g_chip;  enum tpm_duration {  	TPM_SHORT = 0, @@ -45,9 +64,18 @@ enum tpm_duration {  	TPM_UNDEFINED,  }; -#define TPM_MAX_ORDINAL 243 -#define TPM_MAX_PROTECTED_ORDINAL 12 -#define TPM_PROTECTED_ORDINAL_MASK 0xFF +/* Extended error numbers from linux (see errno.h) */ +#define ECANCELED	125	/* Operation Canceled */ + +/* Timer frequency. Corresponds to msec timer resolution*/ +#define HZ		1000 + +#define TPM_MAX_ORDINAL			243 +#define TPM_MAX_PROTECTED_ORDINAL	12 +#define TPM_PROTECTED_ORDINAL_MASK	0xFF + +#define TPM_CMD_COUNT_BYTE	2 +#define TPM_CMD_ORDINAL_BYTE	6  /*   * Array with one entry per ordinal defining the maximum amount @@ -318,34 +346,31 @@ static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = {  	TPM_MEDIUM,  }; -/* - * Returns max number of milliseconds to wait - */ -unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal) +/* Returns max number of milliseconds to wait */ +static unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, +		u32 ordinal)  {  	int duration_idx = TPM_UNDEFINED;  	int duration = 0; -	if (ordinal < TPM_MAX_ORDINAL) +	if (ordinal < TPM_MAX_ORDINAL) {  		duration_idx = tpm_ordinal_duration[ordinal]; -	else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) < -		 TPM_MAX_PROTECTED_ORDINAL) -		duration_idx = -		    tpm_protected_ordinal_duration[ordinal & -						   TPM_PROTECTED_ORDINAL_MASK]; +	} else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) < +			TPM_MAX_PROTECTED_ORDINAL) { +		duration_idx = tpm_protected_ordinal_duration[ +				ordinal & TPM_PROTECTED_ORDINAL_MASK]; +	}  	if (duration_idx != TPM_UNDEFINED)  		duration = chip->vendor.duration[duration_idx]; +  	if (duration <= 0) -		return 2 * 60 * HZ; /*two minutes timeout*/ +		return 2 * 60 * HZ; /* Two minutes timeout */  	else  		return duration;  } -#define TPM_CMD_COUNT_BYTE 2 -#define TPM_CMD_ORDINAL_BYTE 6 - -ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz) +static ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz)  {  	ssize_t rc;  	u32 count, ordinal; @@ -358,18 +383,17 @@ ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz)  	ordinal = get_unaligned_be32(buf + TPM_CMD_ORDINAL_BYTE);  	if (count == 0) { -		dev_err(chip->dev, "no data\n"); +		error("no data\n");  		return -ENODATA;  	}  	if (count > bufsiz) { -		dev_err(chip->dev, -			"invalid count value %x %zx\n", count, bufsiz); +		error("invalid count value %x %zx\n", count, bufsiz);  		return -E2BIG;  	}  	rc = chip->vendor.send(chip, (u8 *)buf, count);  	if (rc < 0) { -		dev_err(chip->dev, "tpm_transmit: tpm_send: error %zd\n", rc); +		error("tpm_transmit: tpm_send: error %zd\n", rc);  		goto out;  	} @@ -379,47 +403,126 @@ ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz)  	start = get_timer(0);  	stop = tpm_calc_ordinal_duration(chip, ordinal);  	do { -		dbg_printf("waiting for status...\n"); +		debug("waiting for status...\n");  		u8 status = chip->vendor.status(chip);  		if ((status & chip->vendor.req_complete_mask) ==  		    chip->vendor.req_complete_val) { -			dbg_printf("...got it;\n"); +			debug("...got it;\n");  			goto out_recv;  		}  		if ((status == chip->vendor.req_canceled)) { -			dev_err(chip->dev, "Operation Canceled\n"); +			error("Operation Canceled\n");  			rc = -ECANCELED;  			goto out;  		} -		msleep(TPM_TIMEOUT); +		udelay(TPM_TIMEOUT * 1000);  	} while (get_timer(start) < stop);  	chip->vendor.cancel(chip); -	dev_err(chip->dev, "Operation Timed out\n"); +	error("Operation Timed out\n");  	rc = -ETIME;  	goto out;  out_recv: - -	dbg_printf("out_recv: reading response...\n"); +	debug("out_recv: reading response...\n");  	rc = chip->vendor.recv(chip, (u8 *)buf, TPM_BUFSIZE);  	if (rc < 0) -		dev_err(chip->dev, "tpm_transmit: tpm_recv: error %zd\n", rc); +		error("tpm_transmit: tpm_recv: error %zd\n", rc); +  out:  	return rc;  } -#define TPM_ERROR_SIZE 10 +static int tpm_open(uint32_t dev_addr) +{ +	int rc; +	if (g_chip.is_open) +		return -EBUSY; +	rc = tpm_vendor_init(dev_addr); +	if (rc < 0) +		g_chip.is_open = 0; +	return rc; +} -enum tpm_capabilities { -	TPM_CAP_PROP = cpu_to_be32(5), -}; +static void tpm_close(void) +{ +	if (g_chip.is_open) { +		tpm_vendor_cleanup(&g_chip); +		g_chip.is_open = 0; +	} +} -enum tpm_sub_capabilities { -	TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115), -	TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120), -}; +static int tpm_select(void) +{ +	int ret; + +	tpm.old_bus = i2c_get_bus_num(); +	if (tpm.old_bus != tpm.i2c_bus) { +		ret = i2c_set_bus_num(tpm.i2c_bus); +		if (ret) { +			debug("%s: Fail to set i2c bus %d\n", __func__, +			      tpm.i2c_bus); +			return -1; +		} +	} +	return 0; +} + +static int tpm_deselect(void) +{ +	int ret; + +	if (tpm.old_bus != i2c_get_bus_num()) { +		ret = i2c_set_bus_num(tpm.old_bus); +		if (ret) { +			debug("%s: Fail to restore i2c bus %d\n", +			      __func__, tpm.old_bus); +			return -1; +		} +	} +	tpm.old_bus = -1; +	return 0; +} + +/** + * Decode TPM configuration. + * + * @param dev	Returns a configuration of TPM device + * @return 0 if ok, -1 on error + */ +static int tpm_decode_config(struct tpm *dev) +{ +#ifdef CONFIG_OF_CONTROL +	const void *blob = gd->fdt_blob; +	int node, parent; +	int i2c_bus; + +	node = fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9635_TPM); +	if (node < 0) { +		node = fdtdec_next_compatible(blob, 0, +				COMPAT_INFINEON_SLB9645_TPM); +	} +	if (node < 0) { +		debug("%s: Node not found\n", __func__); +		return -1; +	} +	parent = fdt_parent_offset(blob, node); +	if (parent < 0) { +		debug("%s: Cannot find node parent\n", __func__); +		return -1; +	} +	i2c_bus = i2c_get_bus_num_fdt(parent); +	if (i2c_bus < 0) +		return -1; +	dev->i2c_bus = i2c_bus; +	dev->slave_addr = fdtdec_get_addr(blob, node, "reg"); +#else +	dev->i2c_bus = CONFIG_TPM_TIS_I2C_BUS_NUMBER; +	dev->slave_addr = CONFIG_TPM_TIS_I2C_SLAVE_ADDRESS; +#endif +	return 0; +}  struct tpm_chip *tpm_register_hardware(const struct tpm_vendor_specific *entry)  { @@ -433,21 +536,94 @@ struct tpm_chip *tpm_register_hardware(const struct tpm_vendor_specific *entry)  	return chip;  } -int tpm_open(uint32_t dev_addr) +int tis_init(void) +{ +	if (tpm.inited) +		return 0; + +	if (tpm_decode_config(&tpm)) +		return -1; + +	if (tpm_select()) +		return -1; + +	/* +	 * Probe TPM twice; the first probing might fail because TPM is asleep, +	 * and the probing can wake up TPM. +	 */ +	if (i2c_probe(tpm.slave_addr) && i2c_probe(tpm.slave_addr)) { +		debug("%s: fail to probe i2c addr 0x%x\n", __func__, +		      tpm.slave_addr); +		return -1; +	} + +	tpm_deselect(); + +	tpm.inited = 1; + +	return 0; +} + +int tis_open(void)  {  	int rc; -	if (g_chip.is_open) -		return -EBUSY; -	rc = tpm_vendor_init(dev_addr); -	if (rc < 0) -		g_chip.is_open = 0; + +	if (!tpm.inited) +		return -1; + +	if (tpm_select()) +		return -1; + +	rc = tpm_open(tpm.slave_addr); + +	tpm_deselect(); +  	return rc;  } -void tpm_close(void) +int tis_close(void)  { -	if (g_chip.is_open) { -		tpm_vendor_cleanup(&g_chip); -		g_chip.is_open = 0; +	if (!tpm.inited) +		return -1; + +	if (tpm_select()) +		return -1; + +	tpm_close(); + +	tpm_deselect(); + +	return 0; +} + +int tis_sendrecv(const uint8_t *sendbuf, size_t sbuf_size, +		uint8_t *recvbuf, size_t *rbuf_len) +{ +	int len; +	uint8_t buf[4096]; + +	if (!tpm.inited) +		return -1; + +	if (sizeof(buf) < sbuf_size) +		return -1; + +	memcpy(buf, sendbuf, sbuf_size); + +	if (tpm_select()) +		return -1; + +	len = tpm_transmit(buf, sbuf_size); + +	tpm_deselect(); + +	if (len < 10) { +		*rbuf_len = 0; +		return -1;  	} + +	memcpy(recvbuf, buf, len); +	*rbuf_len = len; + +	return 0;  } diff --git a/drivers/tpm/slb9635_i2c/tpm.h b/drivers/tpm/tpm_private.h index 9ddee865d..888a074d3 100644 --- a/drivers/tpm/slb9635_i2c/tpm.h +++ b/drivers/tpm/tpm_private.h @@ -33,12 +33,11 @@   * MA 02111-1307 USA   */ -#ifndef _TPM_H_ -#define _TPM_H_ +#ifndef _TPM_PRIVATE_H_ +#define _TPM_PRIVATE_H_  #include <linux/compiler.h> - -#include "compatibility.h" +#include <linux/types.h>  enum tpm_timeout {  	TPM_TIMEOUT = 5,	/* msecs */ @@ -47,13 +46,9 @@ enum tpm_timeout {  /* Size of external transmit buffer (used in tpm_transmit)*/  #define TPM_BUFSIZE 4096 -/* Index of fields in TPM command buffer */ -#define TPM_CMD_SIZE_BYTE 2 -#define TPM_CMD_ORDINAL_BYTE 6 -  /* Index of Count field in TPM response buffer */ -#define TPM_RSP_SIZE_BYTE 2 -#define TPM_RSP_RC_BYTE 6 +#define TPM_RSP_SIZE_BYTE	2 +#define TPM_RSP_RC_BYTE		6  struct tpm_chip; @@ -65,10 +60,10 @@ struct tpm_vendor_specific {  	int (*recv) (struct tpm_chip *, u8 *, size_t);  	int (*send) (struct tpm_chip *, u8 *, size_t);  	void (*cancel) (struct tpm_chip *); -	 u8(*status) (struct tpm_chip *); +	u8(*status) (struct tpm_chip *);  	int locality; -	unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* msec */ -	unsigned long duration[3];	/* msec */ +	unsigned long timeout_a, timeout_b, timeout_c, timeout_d;  /* msec */ +	unsigned long duration[3];  /* msec */  };  struct tpm_chip { @@ -132,30 +127,11 @@ struct tpm_cmd_t {  	union tpm_cmd_params params;  } __packed; +struct tpm_chip *tpm_register_hardware(const struct tpm_vendor_specific *); -/* ---------- Interface for TPM vendor ------------ */ - -extern struct tpm_chip *tpm_register_hardware( -	const struct tpm_vendor_specific *); +int tpm_vendor_init(uint32_t dev_addr); -extern int tpm_vendor_init(uint32_t dev_addr); +void tpm_vendor_cleanup(struct tpm_chip *chip); -extern void tpm_vendor_cleanup(struct tpm_chip *chip); - -/* ---------- Interface for TDDL ------------------- */ - -/* - * if dev_addr != 0 - redefines TPM device address - * Returns < 0 on error, 0 on success. - */ -extern int tpm_open(uint32_t dev_addr); - -extern void tpm_close(void); - -/* - * Transmit bufsiz bytes out of buf to TPM and get results back in buf, too. - * Returns < 0 on error, 0 on success. - */ -extern ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz);  #endif diff --git a/drivers/tpm/slb9635_i2c/tpm_tis_i2c.c b/drivers/tpm/tpm_tis_i2c.c index 82a41bf5b..2dd8501f9 100644 --- a/drivers/tpm/slb9635_i2c/tpm_tis_i2c.c +++ b/drivers/tpm/tpm_tis_i2c.c @@ -37,46 +37,102 @@   */  #include <common.h> +#include <fdtdec.h> +#include <compiler.h>  #include <i2c.h> +#include <tpm.h> +#include <asm-generic/errno.h>  #include <linux/types.h> +#include <linux/unaligned/be_byteshift.h> -#include "compatibility.h" -#include "tpm.h" +#include "tpm_private.h" + +DECLARE_GLOBAL_DATA_PTR; -/* max. buffer size supported by our tpm */ -#ifdef TPM_BUFSIZE -#undef TPM_BUFSIZE -#endif -#define TPM_BUFSIZE 1260  /* Address of the TPM on the I2C bus */ -#define TPM_I2C_ADDR 0x20 -/* max. number of iterations after i2c NAK */ -#define MAX_COUNT 3 +#define TPM_I2C_ADDR		0x20 + +/* Max buffer size supported by our tpm */ +#define TPM_DEV_BUFSIZE		1260 -#define SLEEP_DURATION 60 /*in usec*/ +/* Max number of iterations after i2c NAK */ +#define MAX_COUNT		3 -/* max. number of iterations after i2c NAK for 'long' commands - * we need this especially for sending TPM_READY, since the cleanup after the +/* + * Max number of iterations after i2c NAK for 'long' commands + * + * We need this especially for sending TPM_READY, since the cleanup after the   * transtion to the ready state may take some time, but it is unpredictable   * how long it will take.   */ -#define MAX_COUNT_LONG 50 +#define MAX_COUNT_LONG		50 + +#define SLEEP_DURATION		60	/* in usec */ +#define SLEEP_DURATION_LONG	210	/* in usec */ + +#define TPM_HEADER_SIZE		10 + +/* + * Expected value for DIDVID register + * + * The only device the system knows about at this moment is Infineon slb9635. + */ +#define TPM_TIS_I2C_DID_VID	0x000b15d1L + +enum tis_access { +	TPM_ACCESS_VALID		= 0x80, +	TPM_ACCESS_ACTIVE_LOCALITY	= 0x20, +	TPM_ACCESS_REQUEST_PENDING	= 0x04, +	TPM_ACCESS_REQUEST_USE		= 0x02, +}; + +enum tis_status { +	TPM_STS_VALID			= 0x80, +	TPM_STS_COMMAND_READY		= 0x40, +	TPM_STS_GO			= 0x20, +	TPM_STS_DATA_AVAIL		= 0x10, +	TPM_STS_DATA_EXPECT		= 0x08, +}; -#define SLEEP_DURATION_LONG 210 /* in usec */ +enum tis_defaults { +	TIS_SHORT_TIMEOUT		= 750,	/* ms */ +	TIS_LONG_TIMEOUT		= 2000,	/* ms */ +};  /* expected value for DIDVID register */ -#define TPM_TIS_I2C_DID_VID 0x000b15d1L +#define TPM_TIS_I2C_DID_VID_9635 0x000b15d1L +#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L + +enum i2c_chip_type { +	SLB9635, +	SLB9645, +	UNKNOWN, +}; + +static const char * const chip_name[] = { +	[SLB9635] = "slb9635tt", +	[SLB9645] = "slb9645tt", +	[UNKNOWN] = "unknown/fallback to slb9635", +}; + +#define	TPM_ACCESS(l)			(0x0000 | ((l) << 4)) +#define	TPM_STS(l)			(0x0001 | ((l) << 4)) +#define	TPM_DATA_FIFO(l)		(0x0005 | ((l) << 4)) +#define	TPM_DID_VID(l)			(0x0006 | ((l) << 4))  /* Structure to store I2C TPM specific stuff */ -struct tpm_inf_dev { +struct tpm_dev {  	uint addr; -	u8 buf[TPM_BUFSIZE + sizeof(u8)];	/* max. buffer size + addr */ +	u8 buf[TPM_DEV_BUFSIZE + sizeof(u8)];  /* Max buffer size + addr */ +	enum i2c_chip_type chip_type;  }; -static struct tpm_inf_dev tpm_dev = { +static struct tpm_dev tpm_dev = {  	.addr = TPM_I2C_ADDR  }; +static struct tpm_dev tpm_dev; +  /*   * iic_tpm_read() - read from TPM register   * @addr: register address to read from @@ -91,34 +147,52 @@ static struct tpm_inf_dev tpm_dev = {   *   * Return -EIO on error, 0 on success.   */ -int iic_tpm_read(u8 addr, u8 *buffer, size_t len) +static int iic_tpm_read(u8 addr, u8 *buffer, size_t len)  {  	int rc;  	int count; -	uint myaddr = addr; -	/* we have to use uint here, uchar hangs the board */ +	uint32_t addrbuf = addr; -	for (count = 0; count < MAX_COUNT; count++) { -		rc = i2c_write(tpm_dev.addr, 0, 0, (uchar *)&myaddr, 1); -		if (rc == 0) -			break; /*success, break to skip sleep*/ - -		udelay(SLEEP_DURATION); -	} - -	if (rc) -		return -rc; +	if ((tpm_dev.chip_type == SLB9635) || (tpm_dev.chip_type == UNKNOWN)) { +		/* slb9635 protocol should work in both cases */ +		for (count = 0; count < MAX_COUNT; count++) { +			rc = i2c_write(tpm_dev.addr, 0, 0, +				       (uchar *)&addrbuf, 1); +			if (rc == 0) +				break;  /* Success, break to skip sleep */ +			udelay(SLEEP_DURATION); +		} +		if (rc) +			return -rc; -	/* After the TPM has successfully received the register address it needs -	 * some time, thus we're sleeping here again, before retrieving the data -	 */ -	for (count = 0; count < MAX_COUNT; count++) { -		udelay(SLEEP_DURATION); -		rc = i2c_read(tpm_dev.addr, 0, 0, buffer, len); -		if (rc == 0) -			break; /*success, break to skip sleep*/ +		/* After the TPM has successfully received the register address +		 * it needs some time, thus we're sleeping here again, before +		 * retrieving the data +		 */ +		for (count = 0; count < MAX_COUNT; count++) { +			udelay(SLEEP_DURATION); +			rc = i2c_read(tpm_dev.addr, 0, 0, buffer, len); +			if (rc == 0) +				break;  /* success, break to skip sleep */ +		} +	} else { +		/* +		 * Use a combined read for newer chips. +		 * Unfortunately the smbus functions are not suitable due to +		 * the 32 byte limit of the smbus. +		 * Retries should usually not be needed, but are kept just to +		 * be safe on the safe side. +		 */ +		for (count = 0; count < MAX_COUNT; count++) { +			rc = i2c_read(tpm_dev.addr, addr, 1, buffer, len); +			if (rc == 0) +				break;  /* break here to skip sleep */ +			udelay(SLEEP_DURATION); +		}  	} +	/* Take care of 'guard time' */ +	udelay(SLEEP_DURATION);  	if (rc)  		return -rc; @@ -126,24 +200,24 @@ int iic_tpm_read(u8 addr, u8 *buffer, size_t len)  }  static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, -				unsigned int sleep_time, -				u8 max_count) +		unsigned int sleep_time, u8 max_count)  {  	int rc = 0;  	int count; -	/* prepare send buffer */ +	/* Prepare send buffer */  	tpm_dev.buf[0] = addr;  	memcpy(&(tpm_dev.buf[1]), buffer, len);  	for (count = 0; count < max_count; count++) {  		rc = i2c_write(tpm_dev.addr, 0, 0, tpm_dev.buf, len + 1);  		if (rc == 0) -			break; /*success, break to skip sleep*/ - +			break;  /* Success, break to skip sleep */  		udelay(sleep_time);  	} +	/* take care of 'guard time' */ +	udelay(SLEEP_DURATION);  	if (rc)  		return -rc; @@ -175,42 +249,16 @@ static int iic_tpm_write(u8 addr, u8 *buffer, size_t len)  /*   * This function is needed especially for the cleanup situation after   * sending TPM_READY - * */ + */  static int iic_tpm_write_long(u8 addr, u8 *buffer, size_t len)  {  	return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LONG,  			MAX_COUNT_LONG);  } -#define TPM_HEADER_SIZE 10 - -enum tis_access { -	TPM_ACCESS_VALID = 0x80, -	TPM_ACCESS_ACTIVE_LOCALITY = 0x20, -	TPM_ACCESS_REQUEST_PENDING = 0x04, -	TPM_ACCESS_REQUEST_USE = 0x02, -}; - -enum tis_status { -	TPM_STS_VALID = 0x80, -	TPM_STS_COMMAND_READY = 0x40, -	TPM_STS_GO = 0x20, -	TPM_STS_DATA_AVAIL = 0x10, -	TPM_STS_DATA_EXPECT = 0x08, -}; - -enum tis_defaults { -	TIS_SHORT_TIMEOUT = 750,	/* ms */ -	TIS_LONG_TIMEOUT = 2000,	/* 2 sec */ -}; - -#define	TPM_ACCESS(l)			(0x0000 | ((l) << 4)) -#define	TPM_STS(l)			(0x0001 | ((l) << 4)) -#define	TPM_DATA_FIFO(l)		(0x0005 | ((l) << 4)) -#define	TPM_DID_VID(l)			(0x0006 | ((l) << 4)) -  static int check_locality(struct tpm_chip *chip, int loc)  { +	const u8 mask = TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID;  	u8 buf;  	int rc; @@ -218,8 +266,7 @@ static int check_locality(struct tpm_chip *chip, int loc)  	if (rc < 0)  		return rc; -	if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == -		(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) { +	if ((buf & mask) == mask) {  		chip->vendor.locality = loc;  		return loc;  	} @@ -229,12 +276,13 @@ static int check_locality(struct tpm_chip *chip, int loc)  static void release_locality(struct tpm_chip *chip, int loc, int force)  { +	const u8 mask = TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID;  	u8 buf; +  	if (iic_tpm_read(TPM_ACCESS(loc), &buf, 1) < 0)  		return; -	if (force || (buf & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) == -			(TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) { +	if (force || (buf & mask) == mask) {  		buf = TPM_ACCESS_ACTIVE_LOCALITY;  		iic_tpm_write(TPM_ACCESS(loc), &buf, 1);  	} @@ -246,17 +294,17 @@ static int request_locality(struct tpm_chip *chip, int loc)  	u8 buf = TPM_ACCESS_REQUEST_USE;  	if (check_locality(chip, loc) >= 0) -		return loc; /* we already have the locality */ +		return loc;  /* We already have the locality */  	iic_tpm_write(TPM_ACCESS(loc), &buf, 1); -	/* wait for burstcount */ +	/* Wait for burstcount */  	start = get_timer(0);  	stop = chip->vendor.timeout_a;  	do {  		if (check_locality(chip, loc) >= 0)  			return loc; -		msleep(TPM_TIMEOUT); +		udelay(TPM_TIMEOUT * 1000);  	} while (get_timer(start) < stop);  	return -1; @@ -264,8 +312,9 @@ static int request_locality(struct tpm_chip *chip, int loc)  static u8 tpm_tis_i2c_status(struct tpm_chip *chip)  { -	/* NOTE: since i2c read may fail, return 0 in this case --> time-out */ +	/* NOTE: Since i2c read may fail, return 0 in this case --> time-out */  	u8 buf; +  	if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0)  		return 0;  	else @@ -274,8 +323,9 @@ static u8 tpm_tis_i2c_status(struct tpm_chip *chip)  static void tpm_tis_i2c_ready(struct tpm_chip *chip)  { -	/* this causes the current command to be aborted */ +	/* This causes the current command to be aborted */  	u8 buf = TPM_STS_COMMAND_READY; +  	iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1);  } @@ -283,34 +333,34 @@ static ssize_t get_burstcount(struct tpm_chip *chip)  {  	unsigned long start, stop;  	ssize_t burstcnt; -	u8 buf[3]; +	u8 addr, buf[3]; -	/* wait for burstcount */ -	/* which timeout value, spec has 2 answers (c & d) */ +	/* Wait for burstcount */ +	/* XXX: Which timeout value? Spec has 2 answers (c & d) */  	start = get_timer(0);  	stop = chip->vendor.timeout_d;  	do {  		/* Note: STS is little endian */ -		if (iic_tpm_read(TPM_STS(chip->vendor.locality) + 1, buf, 3) -				< 0) +		addr = TPM_STS(chip->vendor.locality) + 1; +		if (iic_tpm_read(addr, buf, 3) < 0)  			burstcnt = 0;  		else  			burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0];  		if (burstcnt)  			return burstcnt; -		msleep(TPM_TIMEOUT); +		udelay(TPM_TIMEOUT * 1000);  	} while (get_timer(start) < stop);  	return -EBUSY;  }  static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, -			int *status) +		int *status)  {  	unsigned long start, stop; -	/* check current status */ +	/* Check current status */  	*status = tpm_tis_i2c_status(chip);  	if ((*status & mask) == mask)  		return 0; @@ -318,11 +368,10 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,  	start = get_timer(0);  	stop = timeout;  	do { -		msleep(TPM_TIMEOUT); +		udelay(TPM_TIMEOUT * 1000);  		*status = tpm_tis_i2c_status(chip);  		if ((*status & mask) == mask)  			return 0; -  	} while (get_timer(start) < stop);  	return -ETIME; @@ -337,17 +386,16 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)  	while (size < count) {  		burstcnt = get_burstcount(chip); -		/* burstcount < 0 = tpm is busy */ +		/* burstcount < 0 -> tpm is busy */  		if (burstcnt < 0)  			return burstcnt; -		/* limit received data to max. left */ +		/* Limit received data to max left */  		if (burstcnt > (count - size))  			burstcnt = count - size;  		rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality), -				  &(buf[size]), -				  burstcnt); +				&(buf[size]), burstcnt);  		if (rc == 0)  			size += burstcnt;  	} @@ -365,10 +413,10 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)  		goto out;  	} -	/* read first 10 bytes, including tag, paramsize, and result */ +	/* Read first 10 bytes, including tag, paramsize, and result */  	size = recv_data(chip, buf, TPM_HEADER_SIZE);  	if (size < TPM_HEADER_SIZE) { -		dev_err(chip->dev, "Unable to read header\n"); +		error("Unable to read header\n");  		goto out;  	} @@ -379,23 +427,24 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)  	}  	size += recv_data(chip, &buf[TPM_HEADER_SIZE], -				expected - TPM_HEADER_SIZE); +			expected - TPM_HEADER_SIZE);  	if (size < expected) { -		dev_err(chip->dev, "Unable to read remainder of result\n"); +		error("Unable to read remainder of result\n");  		size = -ETIME;  		goto out;  	}  	wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status); -	if (status & TPM_STS_DATA_AVAIL) {	/* retry? */ -		dev_err(chip->dev, "Error left over data\n"); +	if (status & TPM_STS_DATA_AVAIL) {  /* Retry? */ +		error("Error left over data\n");  		size = -EIO;  		goto out;  	}  out:  	tpm_tis_i2c_ready(chip); -	/* The TPM needs some time to clean up here, +	/* +	 * The TPM needs some time to clean up here,  	 * so we sleep rather than keeping the bus busy  	 */  	udelay(2000); @@ -409,10 +458,11 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)  	int rc, status;  	ssize_t burstcnt;  	size_t count = 0; +	int retry = 0;  	u8 sts = TPM_STS_GO; -	if (len > TPM_BUFSIZE) -		return -E2BIG; /* command is too long for our tpm, sorry */ +	if (len > TPM_DEV_BUFSIZE) +		return -E2BIG;  /* Command is too long for our tpm, sorry */  	if (request_locality(chip, 0) < 0)  		return -EBUSY; @@ -420,44 +470,45 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)  	status = tpm_tis_i2c_status(chip);  	if ((status & TPM_STS_COMMAND_READY) == 0) {  		tpm_tis_i2c_ready(chip); -		if (wait_for_stat -		    (chip, TPM_STS_COMMAND_READY, -		     chip->vendor.timeout_b, &status) < 0) { +		if (wait_for_stat(chip, TPM_STS_COMMAND_READY, +				  chip->vendor.timeout_b, &status) < 0) {  			rc = -ETIME;  			goto out_err;  		}  	} -	while (count < len - 1) { -		burstcnt = get_burstcount(chip); +	burstcnt = get_burstcount(chip); -		/* burstcount < 0 = tpm is busy */ -		if (burstcnt < 0) -			return burstcnt; +	/* burstcount < 0 -> tpm is busy */ +	if (burstcnt < 0) +		return burstcnt; -		if (burstcnt > (len-1-count)) -			burstcnt = len-1-count; +	while (count < len - 1) { +		if (burstcnt > len - 1 - count) +			burstcnt = len - 1 - count; -#ifdef CONFIG_TPM_I2C_BURST_LIMITATION -		if (burstcnt > CONFIG_TPM_I2C_BURST_LIMITATION) -			burstcnt = CONFIG_TPM_I2C_BURST_LIMITATION; -#endif /* CONFIG_TPM_I2C_BURST_LIMITATION */ +#ifdef CONFIG_TPM_TIS_I2C_BURST_LIMITATION +		if (retry && burstcnt > CONFIG_TPM_TIS_I2C_BURST_LIMITATION) +			burstcnt = CONFIG_TPM_TIS_I2C_BURST_LIMITATION; +#endif /* CONFIG_TPM_TIS_I2C_BURST_LIMITATION */  		rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), -				   &(buf[count]), burstcnt); +				&(buf[count]), burstcnt);  		if (rc == 0)  			count += burstcnt; +		else { +			retry++; +			wait_for_stat(chip, TPM_STS_VALID, +				      chip->vendor.timeout_c, &status); -		wait_for_stat(chip, TPM_STS_VALID, -			      chip->vendor.timeout_c, &status); - -		if ((status & TPM_STS_DATA_EXPECT) == 0) { -			rc = -EIO; -			goto out_err; +			if ((status & TPM_STS_DATA_EXPECT) == 0) { +				rc = -EIO; +				goto out_err; +			}  		}  	} -	/* write last byte */ +	/* Write last byte */  	iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1);  	wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);  	if ((status & TPM_STS_DATA_EXPECT) != 0) { @@ -465,13 +516,15 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)  		goto out_err;  	} -	/* go and do it */ +	/* Go and do it */  	iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1);  	return len; +  out_err:  	tpm_tis_i2c_ready(chip); -	/* The TPM needs some time to clean up here, +	/* +	 * The TPM needs some time to clean up here,  	 * so we sleep rather than keeping the bus busy  	 */  	udelay(2000); @@ -490,12 +543,26 @@ static struct tpm_vendor_specific tpm_tis_i2c = {  	.req_canceled = TPM_STS_COMMAND_READY,  }; -/* initialisation of i2c tpm */ +static enum i2c_chip_type tpm_vendor_chip_type(void) +{ +#ifdef CONFIG_OF_CONTROL +	const void *blob = gd->fdt_blob; +	if (fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9645_TPM) >= 0) +		return SLB9645; + +	if (fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9635_TPM) >= 0) +		return SLB9635; +#endif +	return UNKNOWN; +} + +/* Initialisation of i2c tpm */  int tpm_vendor_init(uint32_t dev_addr)  {  	u32 vendor; +	u32 expected_did_vid;  	uint old_addr;  	int rc = 0;  	struct tpm_chip *chip; @@ -504,6 +571,8 @@ int tpm_vendor_init(uint32_t dev_addr)  	if (dev_addr != 0)  		tpm_dev.addr = dev_addr; +	tpm_dev.chip_type = tpm_vendor_chip_type(); +  	chip = tpm_register_hardware(&tpm_tis_i2c);  	if (chip < 0) {  		rc = -ENODEV; @@ -519,26 +588,33 @@ int tpm_vendor_init(uint32_t dev_addr)  	chip->vendor.timeout_c = TIS_SHORT_TIMEOUT;  	chip->vendor.timeout_d = TIS_SHORT_TIMEOUT; -	if (request_locality(chip, 0) != 0) { +	if (request_locality(chip, 0) < 0) {  		rc = -ENODEV;  		goto out_err;  	} -	/* read four bytes from DID_VID register */ +	/* Read four bytes from DID_VID register */  	if (iic_tpm_read(TPM_DID_VID(0), (uchar *)&vendor, 4) < 0) {  		rc = -EIO;  		goto out_release;  	} -	/* create DID_VID register value, after swapping to little-endian */ -	vendor = be32_to_cpu(vendor); +	if (tpm_dev.chip_type == SLB9635) { +		vendor = be32_to_cpu(vendor); +		expected_did_vid = TPM_TIS_I2C_DID_VID_9635; +	} else { +		/* device id and byte order has changed for newer i2c tpms */ +		expected_did_vid = TPM_TIS_I2C_DID_VID_9645; +	} -	if (vendor != TPM_TIS_I2C_DID_VID) { +	if (tpm_dev.chip_type != UNKNOWN && vendor != expected_did_vid) { +		error("Vendor id did not match! ID was %08x\n", vendor);  		rc = -ENODEV;  		goto out_release;  	} -	dev_info(dev, "1.2 TPM (device-id 0x%X)\n", vendor >> 16); +	debug("1.2 TPM (chip type %s device-id 0x%X)\n", +	      chip_name[tpm_dev.chip_type], vendor >> 16);  	/*  	 * A timeout query to TPM can be placed here. diff --git a/drivers/tpm/generic_lpc_tpm.c b/drivers/tpm/tpm_tis_lpc.c index 04ad41897..04ad41897 100644 --- a/drivers/tpm/generic_lpc_tpm.c +++ b/drivers/tpm/tpm_tis_lpc.c diff --git a/drivers/usb/eth/asix.c b/drivers/usb/eth/asix.c index 75ec8f788..76624b925 100644 --- a/drivers/usb/eth/asix.c +++ b/drivers/usb/eth/asix.c @@ -407,46 +407,40 @@ static int asix_basic_reset(struct ueth_data *dev)  	rx_ctl = asix_read_rx_ctl(dev);  	debug("RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); -	return 0; -} - -/* - * Asix callbacks - */ -static int asix_init(struct eth_device *eth, bd_t *bd) -{ -	struct ueth_data	*dev = (struct ueth_data *)eth->priv; -	int timeout = 0; -#define TIMEOUT_RESOLUTION 50	/* ms */ -	int link_detected; - -	debug("** %s()\n", __func__); -  	dev->phy_id = asix_get_phy_addr(dev);  	if (dev->phy_id < 0)  		debug("Failed to read phy id\n"); -	if (asix_sw_reset(dev, AX_SWRESET_PRL) < 0) -		goto out_err; - -	if (asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL) < 0) -		goto out_err; -  	asix_mdio_write(dev, dev->phy_id, MII_BMCR, BMCR_RESET);  	asix_mdio_write(dev, dev->phy_id, MII_ADVERTISE,  			ADVERTISE_ALL | ADVERTISE_CSMA);  	mii_nway_restart(dev);  	if (asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT) < 0) -		goto out_err; +		return -1;  	if (asix_write_cmd(dev, AX_CMD_WRITE_IPG0,  				AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,  				AX88772_IPG2_DEFAULT, 0, NULL) < 0) {  		debug("Write IPG,IPG1,IPG2 failed\n"); -		goto out_err; +		return -1;  	} +	return 0; +} + +/* + * Asix callbacks + */ +static int asix_init(struct eth_device *eth, bd_t *bd) +{ +	struct ueth_data	*dev = (struct ueth_data *)eth->priv; +	int timeout = 0; +#define TIMEOUT_RESOLUTION 50	/* ms */ +	int link_detected; + +	debug("** %s()\n", __func__); +  	if (asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL) < 0)  		goto out_err; diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index e545b6be6..432cf178c 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -35,6 +35,7 @@ endif  # new USB gadget layer dependencies  ifdef CONFIG_USB_GADGET  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  endif diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 2c5600ed5..f30778a16 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1098,4 +1098,5 @@ void usb_composite_unregister(struct usb_composite_driver *driver)  	if (composite != driver)  		return;  	usb_gadget_unregister_driver(&composite_driver); +	composite = NULL;  } diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index c28866f7d..45bc132ae 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -2261,7 +2261,8 @@ reset:  	if (rc)  		goto reset;  	fsg->bulk_out_enabled = 1; -	common->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); +	common->bulk_out_maxpacket = +				le16_to_cpu(get_unaligned(&d->wMaxPacketSize));  	clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);  	/* Allocate the requests */ diff --git a/drivers/usb/gadget/fotg210.c b/drivers/usb/gadget/fotg210.c new file mode 100644 index 000000000..d003331ba --- /dev/null +++ b/drivers/usb/gadget/fotg210.c @@ -0,0 +1,948 @@ +/* + * Faraday USB 2.0 OTG Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su <dantesu@faraday-tech.com> + * + * This file is released under the terms of GPL v2 and any later version. + * See the file COPYING in the root directory of the source tree for details. + */ + +#include <common.h> +#include <command.h> +#include <config.h> +#include <net.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <linux/types.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include <usb/fotg210.h> + +#define CFG_NUM_ENDPOINTS		4 +#define CFG_EP0_MAX_PACKET_SIZE	64 +#define CFG_EPX_MAX_PACKET_SIZE	512 + +#define CFG_CMD_TIMEOUT (CONFIG_SYS_HZ >> 2) /* 250 ms */ + +struct fotg210_chip; + +struct fotg210_ep { +	struct usb_ep ep; + +	uint maxpacket; +	uint id; +	uint stopped; + +	struct list_head                      queue; +	struct fotg210_chip                  *chip; +	const struct usb_endpoint_descriptor *desc; +}; + +struct fotg210_request { +	struct usb_request req; +	struct list_head   queue; +	struct fotg210_ep *ep; +}; + +struct fotg210_chip { +	struct usb_gadget         gadget; +	struct usb_gadget_driver *driver; +	struct fotg210_regs      *regs; +	uint8_t                   irq; +	uint16_t                  addr; +	int                       pullup; +	enum usb_device_state     state; +	struct fotg210_ep         ep[1 + CFG_NUM_ENDPOINTS]; +}; + +static struct usb_endpoint_descriptor ep0_desc = { +	.bLength = sizeof(struct usb_endpoint_descriptor), +	.bDescriptorType = USB_DT_ENDPOINT, +	.bEndpointAddress = USB_DIR_IN, +	.bmAttributes = USB_ENDPOINT_XFER_CONTROL, +}; + +static inline int fifo_to_ep(struct fotg210_chip *chip, int id, int in) +{ +	return (id < 0) ? 0 : ((id & 0x03) + 1); +} + +static inline int ep_to_fifo(struct fotg210_chip *chip, int id) +{ +	return (id <= 0) ? -1 : ((id - 1) & 0x03); +} + +static inline int ep_reset(struct fotg210_chip *chip, uint8_t ep_addr) +{ +	int ep = ep_addr & USB_ENDPOINT_NUMBER_MASK; +	struct fotg210_regs *regs = chip->regs; + +	if (ep_addr & USB_DIR_IN) { +		/* reset endpoint */ +		setbits_le32(®s->iep[ep - 1], IEP_RESET); +		mdelay(1); +		clrbits_le32(®s->iep[ep - 1], IEP_RESET); +		/* clear endpoint stall */ +		clrbits_le32(®s->iep[ep - 1], IEP_STALL); +	} else { +		/* reset endpoint */ +		setbits_le32(®s->oep[ep - 1], OEP_RESET); +		mdelay(1); +		clrbits_le32(®s->oep[ep - 1], OEP_RESET); +		/* clear endpoint stall */ +		clrbits_le32(®s->oep[ep - 1], OEP_STALL); +	} + +	return 0; +} + +static int fotg210_reset(struct fotg210_chip *chip) +{ +	struct fotg210_regs *regs = chip->regs; +	uint32_t i; + +	chip->state = USB_STATE_POWERED; + +	/* chip enable */ +	writel(DEVCTRL_EN, ®s->dev_ctrl); + +	/* device address reset */ +	chip->addr = 0; +	writel(0, ®s->dev_addr); + +	/* set idle counter to 7ms */ +	writel(7, ®s->idle); + +	/* disable all interrupts */ +	writel(IMR_MASK, ®s->imr); +	writel(GIMR_MASK, ®s->gimr); +	writel(GIMR0_MASK, ®s->gimr0); +	writel(GIMR1_MASK, ®s->gimr1); +	writel(GIMR2_MASK, ®s->gimr2); + +	/* clear interrupts */ +	writel(ISR_MASK, ®s->isr); +	writel(0, ®s->gisr); +	writel(0, ®s->gisr0); +	writel(0, ®s->gisr1); +	writel(0, ®s->gisr2); + +	/* chip reset */ +	setbits_le32(®s->dev_ctrl, DEVCTRL_RESET); +	mdelay(10); +	if (readl(®s->dev_ctrl) & DEVCTRL_RESET) { +		printf("fotg210: chip reset failed\n"); +		return -1; +	} + +	/* CX FIFO reset */ +	setbits_le32(®s->cxfifo, CXFIFO_CXFIFOCLR); +	mdelay(10); +	if (readl(®s->cxfifo) & CXFIFO_CXFIFOCLR) { +		printf("fotg210: ep0 fifo reset failed\n"); +		return -1; +	} + +	/* create static ep-fifo map (EP1 <-> FIFO0, EP2 <-> FIFO1 ...) */ +	writel(EPMAP14_DEFAULT, ®s->epmap14); +	writel(EPMAP58_DEFAULT, ®s->epmap58); +	writel(FIFOMAP_DEFAULT, ®s->fifomap); +	writel(0, ®s->fifocfg); +	for (i = 0; i < 8; ++i) { +		writel(CFG_EPX_MAX_PACKET_SIZE, ®s->iep[i]); +		writel(CFG_EPX_MAX_PACKET_SIZE, ®s->oep[i]); +	} + +	/* FIFO reset */ +	for (i = 0; i < 4; ++i) { +		writel(FIFOCSR_RESET, ®s->fifocsr[i]); +		mdelay(10); +		if (readl(®s->fifocsr[i]) & FIFOCSR_RESET) { +			printf("fotg210: fifo%d reset failed\n", i); +			return -1; +		} +	} + +	/* enable only device interrupt and triggered at level-high */ +	writel(IMR_IRQLH | IMR_HOST | IMR_OTG, ®s->imr); +	writel(ISR_MASK, ®s->isr); +	/* disable EP0 IN/OUT interrupt */ +	writel(GIMR0_CXOUT | GIMR0_CXIN, ®s->gimr0); +	/* disable EPX IN+SPK+OUT interrupts */ +	writel(GIMR1_MASK, ®s->gimr1); +	/* disable wakeup+idle+dma+zlp interrupts */ +	writel(GIMR2_WAKEUP | GIMR2_IDLE | GIMR2_DMAERR | GIMR2_DMAFIN +		| GIMR2_ZLPRX | GIMR2_ZLPTX, ®s->gimr2); +	/* enable all group interrupt */ +	writel(0, ®s->gimr); + +	/* suspend delay = 3 ms */ +	writel(3, ®s->idle); + +	/* turn-on device interrupts */ +	setbits_le32(®s->dev_ctrl, DEVCTRL_GIRQ_EN); + +	return 0; +} + +static inline int fotg210_cxwait(struct fotg210_chip *chip, uint32_t mask) +{ +	struct fotg210_regs *regs = chip->regs; +	int ret = -1; +	ulong ts; + +	for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) { +		if ((readl(®s->cxfifo) & mask) != mask) +			continue; +		ret = 0; +		break; +	} + +	if (ret) +		printf("fotg210: cx/ep0 timeout\n"); + +	return ret; +} + +static int fotg210_dma(struct fotg210_ep *ep, struct fotg210_request *req) +{ +	struct fotg210_chip *chip = ep->chip; +	struct fotg210_regs *regs = chip->regs; +	uint32_t tmp, ts; +	uint8_t *buf  = req->req.buf + req->req.actual; +	uint32_t len  = req->req.length - req->req.actual; +	int fifo = ep_to_fifo(chip, ep->id); +	int ret = -EBUSY; + +	/* 1. init dma buffer */ +	if (len > ep->maxpacket) +		len = ep->maxpacket; + +	/* 2. wait for dma ready (hardware) */ +	for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) { +		if (!(readl(®s->dma_ctrl) & DMACTRL_START)) { +			ret = 0; +			break; +		} +	} +	if (ret) { +		printf("fotg210: dma busy\n"); +		req->req.status = ret; +		return ret; +	} + +	/* 3. DMA target setup */ +	if (ep->desc->bEndpointAddress & USB_DIR_IN) +		flush_dcache_range((ulong)buf, (ulong)buf + len); +	else +		invalidate_dcache_range((ulong)buf, (ulong)buf + len); + +	writel(virt_to_phys(buf), ®s->dma_addr); + +	if (ep->desc->bEndpointAddress & USB_DIR_IN) { +		if (ep->id == 0) { +			/* Wait until cx/ep0 fifo empty */ +			fotg210_cxwait(chip, CXFIFO_CXFIFOE); +			writel(DMAFIFO_CX, ®s->dma_fifo); +		} else { +			/* Wait until epx fifo empty */ +			fotg210_cxwait(chip, CXFIFO_FIFOE(fifo)); +			writel(DMAFIFO_FIFO(fifo), ®s->dma_fifo); +		} +		writel(DMACTRL_LEN(len) | DMACTRL_MEM2FIFO, ®s->dma_ctrl); +	} else { +		uint32_t blen; + +		if (ep->id == 0) { +			writel(DMAFIFO_CX, ®s->dma_fifo); +			do { +				blen = CXFIFO_BYTES(readl(®s->cxfifo)); +			} while (blen < len); +		} else { +			writel(DMAFIFO_FIFO(fifo), ®s->dma_fifo); +			blen = FIFOCSR_BYTES(readl(®s->fifocsr[fifo])); +		} +		len  = (len < blen) ? len : blen; +		writel(DMACTRL_LEN(len) | DMACTRL_FIFO2MEM, ®s->dma_ctrl); +	} + +	/* 4. DMA start */ +	setbits_le32(®s->dma_ctrl, DMACTRL_START); + +	/* 5. DMA wait */ +	ret = -EBUSY; +	for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) { +		tmp = readl(®s->gisr2); +		/* DMA complete */ +		if (tmp & GISR2_DMAFIN) { +			ret = 0; +			break; +		} +		/* DMA error */ +		if (tmp & GISR2_DMAERR) { +			printf("fotg210: dma error\n"); +			break; +		} +		/* resume, suspend, reset */ +		if (tmp & (GISR2_RESUME | GISR2_SUSPEND | GISR2_RESET)) { +			printf("fotg210: dma reset by host\n"); +			break; +		} +	} + +	/* 7. DMA target reset */ +	if (ret) +		writel(DMACTRL_ABORT | DMACTRL_CLRFF, ®s->dma_ctrl); + +	writel(0, ®s->gisr2); +	writel(0, ®s->dma_fifo); + +	req->req.status = ret; +	if (!ret) +		req->req.actual += len; +	else +		printf("fotg210: ep%d dma error(code=%d)\n", ep->id, ret); + +	return len; +} + +/* + * result of setup packet + */ +#define CX_IDLE		0 +#define CX_FINISH	1 +#define CX_STALL	2 + +static void fotg210_setup(struct fotg210_chip *chip) +{ +	int id, ret = CX_IDLE; +	uint32_t tmp[2]; +	struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)tmp; +	struct fotg210_regs *regs = chip->regs; + +	/* +	 * If this is the first Cx 8 byte command, +	 * we can now query USB mode (high/full speed; USB 2.0/USB 1.0) +	 */ +	if (chip->state == USB_STATE_POWERED) { +		chip->state = USB_STATE_DEFAULT; +		if (readl(®s->otgcsr) & OTGCSR_DEV_B) { +			/* Mini-B */ +			if (readl(®s->dev_ctrl) & DEVCTRL_HS) { +				puts("fotg210: HS\n"); +				chip->gadget.speed = USB_SPEED_HIGH; +				/* SOF mask timer = 1100 ticks */ +				writel(SOFMTR_TMR(1100), ®s->sof_mtr); +			} else { +				puts("fotg210: FS\n"); +				chip->gadget.speed = USB_SPEED_FULL; +				/* SOF mask timer = 10000 ticks */ +				writel(SOFMTR_TMR(10000), ®s->sof_mtr); +			} +		} else { +			printf("fotg210: mini-A?\n"); +		} +	} + +	/* switch data port to ep0 */ +	writel(DMAFIFO_CX, ®s->dma_fifo); +	/* fetch 8 bytes setup packet */ +	tmp[0] = readl(®s->ep0_data); +	tmp[1] = readl(®s->ep0_data); +	/* release data port */ +	writel(0, ®s->dma_fifo); + +	if (req->bRequestType & USB_DIR_IN) +		ep0_desc.bEndpointAddress = USB_DIR_IN; +	else +		ep0_desc.bEndpointAddress = USB_DIR_OUT; + +	ret = CX_IDLE; + +	if ((req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { +		switch (req->bRequest) { +		case USB_REQ_SET_CONFIGURATION: +			debug("fotg210: set_cfg(%d)\n", req->wValue & 0x00FF); +			if (!(req->wValue & 0x00FF)) { +				chip->state = USB_STATE_ADDRESS; +				writel(chip->addr, ®s->dev_addr); +			} else { +				chip->state = USB_STATE_CONFIGURED; +				writel(chip->addr | DEVADDR_CONF, +					®s->dev_addr); +			} +			ret = CX_IDLE; +			break; + +		case USB_REQ_SET_ADDRESS: +			debug("fotg210: set_addr(0x%04X)\n", req->wValue); +			chip->state = USB_STATE_ADDRESS; +			chip->addr  = req->wValue & DEVADDR_ADDR_MASK; +			ret = CX_FINISH; +			writel(chip->addr, ®s->dev_addr); +			break; + +		case USB_REQ_CLEAR_FEATURE: +			debug("fotg210: clr_feature(%d, %d)\n", +				req->bRequestType & 0x03, req->wValue); +			switch (req->wValue) { +			case 0:    /* [Endpoint] halt */ +				ep_reset(chip, req->wIndex); +				ret = CX_FINISH; +				break; +			case 1:    /* [Device] remote wake-up */ +			case 2:    /* [Device] test mode */ +			default: +				ret = CX_STALL; +				break; +			} +			break; + +		case USB_REQ_SET_FEATURE: +			debug("fotg210: set_feature(%d, %d)\n", +				req->wValue, req->wIndex & 0xf); +			switch (req->wValue) { +			case 0:    /* Endpoint Halt */ +				id = req->wIndex & 0xf; +				setbits_le32(®s->iep[id - 1], IEP_STALL); +				setbits_le32(®s->oep[id - 1], OEP_STALL); +				ret = CX_FINISH; +				break; +			case 1:    /* Remote Wakeup */ +			case 2:    /* Test Mode */ +			default: +				ret = CX_STALL; +				break; +			} +			break; + +		case USB_REQ_GET_STATUS: +			debug("fotg210: get_status\n"); +			ret = CX_STALL; +			break; + +		case USB_REQ_SET_DESCRIPTOR: +			debug("fotg210: set_descriptor\n"); +			ret = CX_STALL; +			break; + +		case USB_REQ_SYNCH_FRAME: +			debug("fotg210: sync frame\n"); +			ret = CX_STALL; +			break; +		} +	} /* if ((req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) */ + +	if (ret == CX_IDLE && chip->driver->setup) { +		if (chip->driver->setup(&chip->gadget, req) < 0) +			ret = CX_STALL; +		else +			ret = CX_FINISH; +	} + +	switch (ret) { +	case CX_FINISH: +		setbits_le32(®s->cxfifo, CXFIFO_CXFIN); +		break; + +	case CX_STALL: +		setbits_le32(®s->cxfifo, CXFIFO_CXSTALL | CXFIFO_CXFIN); +		printf("fotg210: cx_stall!\n"); +		break; + +	case CX_IDLE: +		debug("fotg210: cx_idle?\n"); +	default: +		break; +	} +} + +/* + * fifo - FIFO id + * zlp  - zero length packet + */ +static void fotg210_recv(struct fotg210_chip *chip, int ep_id) +{ +	struct fotg210_regs *regs = chip->regs; +	struct fotg210_ep *ep = chip->ep + ep_id; +	struct fotg210_request *req; +	int len; + +	if (ep->stopped || (ep->desc->bEndpointAddress & USB_DIR_IN)) { +		printf("fotg210: ep%d recv, invalid!\n", ep->id); +		return; +	} + +	if (list_empty(&ep->queue)) { +		printf("fotg210: ep%d recv, drop!\n", ep->id); +		return; +	} + +	req = list_first_entry(&ep->queue, struct fotg210_request, queue); +	len = fotg210_dma(ep, req); +	if (len < ep->ep.maxpacket || req->req.length <= req->req.actual) { +		list_del_init(&req->queue); +		if (req->req.complete) +			req->req.complete(&ep->ep, &req->req); +	} + +	if (ep->id > 0 && list_empty(&ep->queue)) { +		setbits_le32(®s->gimr1, +			GIMR1_FIFO_RX(ep_to_fifo(chip, ep->id))); +	} +} + +/* + * USB Gadget Layer + */ +static int fotg210_ep_enable( +	struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ +	struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); +	struct fotg210_chip *chip = ep->chip; +	struct fotg210_regs *regs = chip->regs; +	int id = ep_to_fifo(chip, ep->id); +	int in = (desc->bEndpointAddress & USB_DIR_IN) ? 1 : 0; + +	if (!_ep || !desc +		|| desc->bDescriptorType != USB_DT_ENDPOINT +		|| le16_to_cpu(desc->wMaxPacketSize) == 0) { +		printf("fotg210: bad ep or descriptor\n"); +		return -EINVAL; +	} + +	ep->desc = desc; +	ep->stopped = 0; + +	if (in) +		setbits_le32(®s->fifomap, FIFOMAP(id, FIFOMAP_IN)); + +	switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { +	case USB_ENDPOINT_XFER_CONTROL: +		return -EINVAL; + +	case USB_ENDPOINT_XFER_ISOC: +		setbits_le32(®s->fifocfg, +			FIFOCFG(id, FIFOCFG_EN | FIFOCFG_ISOC)); +		break; + +	case USB_ENDPOINT_XFER_BULK: +		setbits_le32(®s->fifocfg, +			FIFOCFG(id, FIFOCFG_EN | FIFOCFG_BULK)); +		break; + +	case USB_ENDPOINT_XFER_INT: +		setbits_le32(®s->fifocfg, +			FIFOCFG(id, FIFOCFG_EN | FIFOCFG_INTR)); +		break; +	} + +	return 0; +} + +static int fotg210_ep_disable(struct usb_ep *_ep) +{ +	struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); +	struct fotg210_chip *chip = ep->chip; +	struct fotg210_regs *regs = chip->regs; +	int id = ep_to_fifo(chip, ep->id); + +	ep->desc = NULL; +	ep->stopped = 1; + +	clrbits_le32(®s->fifocfg, FIFOCFG(id, FIFOCFG_CFG_MASK)); +	clrbits_le32(®s->fifomap, FIFOMAP(id, FIFOMAP_DIR_MASK)); + +	return 0; +} + +static struct usb_request *fotg210_ep_alloc_request( +	struct usb_ep *_ep, gfp_t gfp_flags) +{ +	struct fotg210_request *req = malloc(sizeof(*req)); + +	if (req) { +		memset(req, 0, sizeof(*req)); +		INIT_LIST_HEAD(&req->queue); +	} +	return &req->req; +} + +static void fotg210_ep_free_request( +	struct usb_ep *_ep, struct usb_request *_req) +{ +	struct fotg210_request *req; + +	req = container_of(_req, struct fotg210_request, req); +	free(req); +} + +static int fotg210_ep_queue( +	struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ +	struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); +	struct fotg210_chip *chip = ep->chip; +	struct fotg210_regs *regs = chip->regs; +	struct fotg210_request *req; + +	req = container_of(_req, struct fotg210_request, req); +	if (!_req || !_req->complete || !_req->buf +		|| !list_empty(&req->queue)) { +		printf("fotg210: invalid request to ep%d\n", ep->id); +		return -EINVAL; +	} + +	if (!chip || chip->state == USB_STATE_SUSPENDED) { +		printf("fotg210: request while chip suspended\n"); +		return -EINVAL; +	} + +	req->req.actual = 0; +	req->req.status = -EINPROGRESS; + +	if (req->req.length == 0) { +		req->req.status = 0; +		if (req->req.complete) +			req->req.complete(&ep->ep, &req->req); +		return 0; +	} + +	if (ep->id == 0) { +		do { +			int len = fotg210_dma(ep, req); +			if (len < ep->ep.maxpacket) +				break; +			if (ep->desc->bEndpointAddress & USB_DIR_IN) +				udelay(100); +		} while (req->req.length > req->req.actual); +	} else { +		if (ep->desc->bEndpointAddress & USB_DIR_IN) { +			do { +				int len = fotg210_dma(ep, req); +				if (len < ep->ep.maxpacket) +					break; +			} while (req->req.length > req->req.actual); +		} else { +			list_add_tail(&req->queue, &ep->queue); +			clrbits_le32(®s->gimr1, +				GIMR1_FIFO_RX(ep_to_fifo(chip, ep->id))); +		} +	} + +	if (ep->id == 0 || (ep->desc->bEndpointAddress & USB_DIR_IN)) { +		if (req->req.complete) +			req->req.complete(&ep->ep, &req->req); +	} + +	return 0; +} + +static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ +	struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); +	struct fotg210_request *req; + +	/* make sure it's actually queued on this endpoint */ +	list_for_each_entry(req, &ep->queue, queue) { +		if (&req->req == _req) +			break; +	} +	if (&req->req != _req) +		return -EINVAL; + +	/* remove the request */ +	list_del_init(&req->queue); + +	/* update status & invoke complete callback */ +	if (req->req.status == -EINPROGRESS) { +		req->req.status = -ECONNRESET; +		if (req->req.complete) +			req->req.complete(_ep, &req->req); +	} + +	return 0; +} + +static int fotg210_ep_halt(struct usb_ep *_ep, int halt) +{ +	struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); +	struct fotg210_chip *chip = ep->chip; +	struct fotg210_regs *regs = chip->regs; +	int ret = -1; + +	debug("fotg210: ep%d halt=%d\n", ep->id, halt); + +	/* Endpoint STALL */ +	if (ep->id > 0 && ep->id <= CFG_NUM_ENDPOINTS) { +		if (halt) { +			/* wait until all ep fifo empty */ +			fotg210_cxwait(chip, 0xf00); +			/* stall */ +			if (ep->desc->bEndpointAddress & USB_DIR_IN) { +				setbits_le32(®s->iep[ep->id - 1], +					IEP_STALL); +			} else { +				setbits_le32(®s->oep[ep->id - 1], +					OEP_STALL); +			} +		} else { +			if (ep->desc->bEndpointAddress & USB_DIR_IN) { +				clrbits_le32(®s->iep[ep->id - 1], +					IEP_STALL); +			} else { +				clrbits_le32(®s->oep[ep->id - 1], +					OEP_STALL); +			} +		} +		ret = 0; +	} + +	return ret; +} + +/* + * activate/deactivate link with host. + */ +static void pullup(struct fotg210_chip *chip, int is_on) +{ +	struct fotg210_regs *regs = chip->regs; + +	if (is_on) { +		if (!chip->pullup) { +			chip->state = USB_STATE_POWERED; +			chip->pullup = 1; +			/* enable the chip */ +			setbits_le32(®s->dev_ctrl, DEVCTRL_EN); +			/* clear unplug bit (BIT0) */ +			clrbits_le32(®s->phy_tmsr, PHYTMSR_UNPLUG); +		} +	} else { +		chip->state = USB_STATE_NOTATTACHED; +		chip->pullup = 0; +		chip->addr = 0; +		writel(chip->addr, ®s->dev_addr); +		/* set unplug bit (BIT0) */ +		setbits_le32(®s->phy_tmsr, PHYTMSR_UNPLUG); +		/* disable the chip */ +		clrbits_le32(®s->dev_ctrl, DEVCTRL_EN); +	} +} + +static int fotg210_pullup(struct usb_gadget *_gadget, int is_on) +{ +	struct fotg210_chip *chip; + +	chip = container_of(_gadget, struct fotg210_chip, gadget); + +	debug("fotg210: pullup=%d\n", is_on); + +	pullup(chip, is_on); + +	return 0; +} + +static int fotg210_get_frame(struct usb_gadget *_gadget) +{ +	struct fotg210_chip *chip; +	struct fotg210_regs *regs; + +	chip = container_of(_gadget, struct fotg210_chip, gadget); +	regs = chip->regs; + +	return SOFFNR_FNR(readl(®s->sof_fnr)); +} + +static struct usb_gadget_ops fotg210_gadget_ops = { +	.get_frame = fotg210_get_frame, +	.pullup = fotg210_pullup, +}; + +static struct usb_ep_ops fotg210_ep_ops = { +	.enable         = fotg210_ep_enable, +	.disable        = fotg210_ep_disable, +	.queue          = fotg210_ep_queue, +	.dequeue        = fotg210_ep_dequeue, +	.set_halt       = fotg210_ep_halt, +	.alloc_request  = fotg210_ep_alloc_request, +	.free_request   = fotg210_ep_free_request, +}; + +static struct fotg210_chip controller = { +	.regs = (void __iomem *)CONFIG_FOTG210_BASE, +	.gadget = { +		.name = "fotg210_udc", +		.ops = &fotg210_gadget_ops, +		.ep0 = &controller.ep[0].ep, +		.speed = USB_SPEED_UNKNOWN, +		.is_dualspeed = 1, +		.is_otg = 0, +		.is_a_peripheral = 0, +		.b_hnp_enable = 0, +		.a_hnp_support = 0, +		.a_alt_hnp_support = 0, +	}, +	.ep[0] = { +		.id = 0, +		.ep = { +			.name  = "ep0", +			.ops   = &fotg210_ep_ops, +		}, +		.desc      = &ep0_desc, +		.chip      = &controller, +		.maxpacket = CFG_EP0_MAX_PACKET_SIZE, +	}, +	.ep[1] = { +		.id = 1, +		.ep = { +			.name  = "ep1", +			.ops   = &fotg210_ep_ops, +		}, +		.chip      = &controller, +		.maxpacket = CFG_EPX_MAX_PACKET_SIZE, +	}, +	.ep[2] = { +		.id = 2, +		.ep = { +			.name  = "ep2", +			.ops   = &fotg210_ep_ops, +		}, +		.chip      = &controller, +		.maxpacket = CFG_EPX_MAX_PACKET_SIZE, +	}, +	.ep[3] = { +		.id = 3, +		.ep = { +			.name  = "ep3", +			.ops   = &fotg210_ep_ops, +		}, +		.chip      = &controller, +		.maxpacket = CFG_EPX_MAX_PACKET_SIZE, +	}, +	.ep[4] = { +		.id = 4, +		.ep = { +			.name  = "ep4", +			.ops   = &fotg210_ep_ops, +		}, +		.chip      = &controller, +		.maxpacket = CFG_EPX_MAX_PACKET_SIZE, +	}, +}; + +int usb_gadget_handle_interrupts(void) +{ +	struct fotg210_chip *chip = &controller; +	struct fotg210_regs *regs = chip->regs; +	uint32_t id, st, isr, gisr; + +	isr  = readl(®s->isr) & (~readl(®s->imr)); +	gisr = readl(®s->gisr) & (~readl(®s->gimr)); +	if (!(isr & ISR_DEV) || !gisr) +		return 0; + +	writel(ISR_DEV, ®s->isr); + +	/* CX interrupts */ +	if (gisr & GISR_GRP0) { +		st = readl(®s->gisr0); +		writel(0, ®s->gisr0); + +		if (st & GISR0_CXERR) +			printf("fotg210: cmd error\n"); + +		if (st & GISR0_CXABORT) +			printf("fotg210: cmd abort\n"); + +		if (st & GISR0_CXSETUP)    /* setup */ +			fotg210_setup(chip); +		else if (st & GISR0_CXEND) /* command finish */ +			setbits_le32(®s->cxfifo, CXFIFO_CXFIN); +	} + +	/* FIFO interrupts */ +	if (gisr & GISR_GRP1) { +		st = readl(®s->gisr1); +		for (id = 0; id < 4; ++id) { +			if (st & GISR1_RX_FIFO(id)) +				fotg210_recv(chip, fifo_to_ep(chip, id, 0)); +		} +	} + +	/* Device Status Interrupts */ +	if (gisr & GISR_GRP2) { +		st = readl(®s->gisr2); +		writel(0, ®s->gisr2); + +		if (st & GISR2_RESET) +			printf("fotg210: reset by host\n"); +		else if (st & GISR2_SUSPEND) +			printf("fotg210: suspend/removed\n"); +		else if (st & GISR2_RESUME) +			printf("fotg210: resume\n"); + +		/* Errors */ +		if (st & GISR2_ISOCERR) +			printf("fotg210: iso error\n"); +		if (st & GISR2_ISOCABT) +			printf("fotg210: iso abort\n"); +		if (st & GISR2_DMAERR) +			printf("fotg210: dma error\n"); +	} + +	return 0; +} + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ +	int i, ret = 0; +	struct fotg210_chip *chip = &controller; + +	if (!driver    || !driver->bind || !driver->setup) { +		puts("fotg210: bad parameter.\n"); +		return -EINVAL; +	} + +	INIT_LIST_HEAD(&chip->gadget.ep_list); +	for (i = 0; i < CFG_NUM_ENDPOINTS + 1; ++i) { +		struct fotg210_ep *ep = chip->ep + i; + +		ep->ep.maxpacket = ep->maxpacket; +		INIT_LIST_HEAD(&ep->queue); + +		if (ep->id == 0) { +			ep->stopped = 0; +		} else { +			ep->stopped = 1; +			list_add_tail(&ep->ep.ep_list, &chip->gadget.ep_list); +		} +	} + +	if (fotg210_reset(chip)) { +		puts("fotg210: reset failed.\n"); +		return -EINVAL; +	} + +	ret = driver->bind(&chip->gadget); +	if (ret) { +		debug("fotg210: driver->bind() returned %d\n", ret); +		return ret; +	} +	chip->driver = driver; + +	return ret; +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ +	struct fotg210_chip *chip = &controller; + +	driver->unbind(&chip->gadget); +	chip->driver = NULL; + +	pullup(chip, 0); + +	return 0; +} diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index e5701422f..f038747e6 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -150,6 +150,12 @@  #define gadget_is_mv(g)        0  #endif +#ifdef CONFIG_USB_GADGET_FOTG210 +#define gadget_is_fotg210(g)        (!strcmp("fotg210_udc", (g)->name)) +#else +#define gadget_is_fotg210(g)        0 +#endif +  /*   * CONFIG_USB_GADGET_SX2   * CONFIG_USB_GADGET_AU1X00 @@ -215,5 +221,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)  		return 0x20;  	else if (gadget_is_mv(gadget))  		return 0x21; +	else if (gadget_is_fotg210(gadget)) +		return 0x22;  	return -ENOENT;  } diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 9ce98f076..085503dbe 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -314,7 +314,8 @@ static int pxa25x_ep_enable(struct usb_ep *_ep,  	if (!_ep || !desc || ep->desc || _ep->name == ep0name  			|| desc->bDescriptorType != USB_DT_ENDPOINT  			|| ep->bEndpointAddress != desc->bEndpointAddress -			|| ep->fifo_size < le16_to_cpu(desc->wMaxPacketSize)) { +			|| ep->fifo_size < +			   le16_to_cpu(get_unaligned(&desc->wMaxPacketSize))) {  		printf("%s, bad ep or descriptor\n", __func__);  		return -EINVAL;  	} @@ -329,9 +330,9 @@ static int pxa25x_ep_enable(struct usb_ep *_ep,  	/* hardware _could_ do smaller, but driver doesn't */  	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK -				&& le16_to_cpu(desc->wMaxPacketSize) +			&& le16_to_cpu(get_unaligned(&desc->wMaxPacketSize))  						!= BULK_FIFO_SIZE) -			|| !desc->wMaxPacketSize) { +			|| !get_unaligned(&desc->wMaxPacketSize)) {  		printf("%s, bad %s maxpacket\n", __func__, _ep->name);  		return -ERANGE;  	} @@ -345,7 +346,7 @@ static int pxa25x_ep_enable(struct usb_ep *_ep,  	ep->desc = desc;  	ep->stopped = 0;  	ep->pio_irqs = 0; -	ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); +	ep->ep.maxpacket = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize));  	/* flush fifo (mostly for OUT buffers) */  	pxa25x_ep_fifo_flush(_ep); @@ -485,7 +486,7 @@ write_fifo(struct pxa25x_ep *ep, struct pxa25x_request *req)  {  	unsigned max; -	max = le16_to_cpu(ep->desc->wMaxPacketSize); +	max = le16_to_cpu(get_unaligned(&ep->desc->wMaxPacketSize));  	do {  		unsigned count;  		int is_last, is_short; @@ -766,7 +767,7 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)  	 */  	if (unlikely(ep->bmAttributes == USB_ENDPOINT_XFER_ISOC  			&& req->req.length > -			le16_to_cpu(ep->desc->wMaxPacketSize))) +			le16_to_cpu(get_unaligned(&ep->desc->wMaxPacketSize))))  		return -EMSGSIZE;  	debug_cond(NOISY, "%s queue req %p, len %d buf %p\n", diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 87a59704d..98f2a104b 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -43,6 +43,7 @@ COBJS-$(CONFIG_USB_EHCI_FSL) += ehci-mpc512x.o  else  COBJS-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o  endif +COBJS-$(CONFIG_USB_EHCI_FARADAY) += ehci-faraday.o  COBJS-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o  COBJS-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o  COBJS-$(CONFIG_USB_EHCI_MXS) += ehci-mxs.o diff --git a/drivers/usb/host/ehci-faraday.c b/drivers/usb/host/ehci-faraday.c new file mode 100644 index 000000000..86add36ce --- /dev/null +++ b/drivers/usb/host/ehci-faraday.c @@ -0,0 +1,148 @@ +/* + * Faraday USB 2.0 EHCI Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su <dantesu@faraday-tech.com> + * + * This file is released under the terms of GPL v2 and any later version. + * See the file COPYING in the root directory of the source tree for details. + */ + +#include <common.h> +#include <asm/io.h> +#include <usb.h> +#include <usb/fusbh200.h> +#include <usb/fotg210.h> + +#include "ehci.h" + +#ifndef CONFIG_USB_EHCI_BASE_LIST +#define CONFIG_USB_EHCI_BASE_LIST	{ CONFIG_USB_EHCI_BASE } +#endif + +union ehci_faraday_regs { +	struct fusbh200_regs usb; +	struct fotg210_regs  otg; +}; + +static inline int ehci_is_fotg2xx(union ehci_faraday_regs *regs) +{ +	return !readl(®s->usb.easstr); +} + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, struct ehci_hccr **ret_hccr, +		struct ehci_hcor **ret_hcor) +{ +	struct ehci_hccr *hccr; +	struct ehci_hcor *hcor; +	union ehci_faraday_regs *regs; +	uint32_t base_list[] = CONFIG_USB_EHCI_BASE_LIST; + +	if (index < 0 || index >= ARRAY_SIZE(base_list)) +		return -1; +	regs = (void __iomem *)base_list[index]; +	hccr = (struct ehci_hccr *)®s->usb.hccr; +	hcor = (struct ehci_hcor *)®s->usb.hcor; + +	if (ehci_is_fotg2xx(regs)) { +		/* A-device bus reset */ +		/* ... Power off A-device */ +		setbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSDROP); +		/* ... Drop vbus and bus traffic */ +		clrbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSREQ); +		mdelay(1); +		/* ... Power on A-device */ +		clrbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSDROP); +		/* ... Drive vbus and bus traffic */ +		setbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSREQ); +		mdelay(1); +		/* Disable OTG & DEV interrupts, triggered at level-high */ +		writel(IMR_IRQLH | IMR_OTG | IMR_DEV, ®s->otg.imr); +		/* Clear all interrupt status */ +		writel(ISR_HOST | ISR_OTG | ISR_DEV, ®s->otg.isr); +	} else { +		/* Interrupt=level-high */ +		setbits_le32(®s->usb.bmcsr, BMCSR_IRQLH); +		/* VBUS on */ +		clrbits_le32(®s->usb.bmcsr, BMCSR_VBUS_OFF); +		/* Disable all interrupts */ +		writel(0x00, ®s->usb.bmier); +		writel(0x1f, ®s->usb.bmisr); +	} + +	*ret_hccr = hccr; +	*ret_hcor = hcor; + +	return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ +	return 0; +} + +/* + * This ehci_set_usbmode() overrides the weak function + * in "ehci-hcd.c". + */ +void ehci_set_usbmode(int index) +{ +	/* nothing needs to be done */ +} + +/* + * This ehci_get_port_speed() overrides the weak function + * in "ehci-hcd.c". + */ +int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) +{ +	int spd, ret = PORTSC_PSPD_HS; +	union ehci_faraday_regs *regs = (void __iomem *)((ulong)hcor - 0x10); + +	if (ehci_is_fotg2xx(regs)) +		spd = OTGCSR_SPD(readl(®s->otg.otgcsr)); +	else +		spd = BMCSR_SPD(readl(®s->usb.bmcsr)); + +	switch (spd) { +	case 0:    /* full speed */ +		ret = PORTSC_PSPD_FS; +		break; +	case 1:    /* low  speed */ +		ret = PORTSC_PSPD_LS; +		break; +	case 2:    /* high speed */ +		ret = PORTSC_PSPD_HS; +		break; +	default: +		printf("ehci-faraday: invalid device speed\n"); +		break; +	} + +	return ret; +} + +/* + * This ehci_get_portsc_register() overrides the weak function + * in "ehci-hcd.c". + */ +uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port) +{ +	/* Faraday EHCI has one and only one portsc register */ +	if (port) { +		/* Printing the message would cause a scan failure! */ +		debug("The request port(%d) is not configured\n", port); +		return NULL; +	} + +	/* Faraday EHCI PORTSC register offset is 0x20 from hcor */ +	return (uint32_t *)((uint8_t *)hcor + 0x20); +} diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index e0f3e4b6c..706cf0cb7 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -589,10 +589,12 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,  		dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(token);  	} else {  		dev->act_len = 0; +#ifndef CONFIG_USB_EHCI_FARADAY  		debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n",  		      dev->devnum, ehci_readl(&ctrl->hcor->or_usbsts),  		      ehci_readl(&ctrl->hcor->or_portsc[0]),  		      ehci_readl(&ctrl->hcor->or_portsc[1])); +#endif  	}  	free(qtd); @@ -603,6 +605,17 @@ fail:  	return -1;  } +__weak uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port) +{ +	if (port < 0 || port >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { +		/* Printing the message would cause a scan failure! */ +		debug("The request port(%u) is not configured\n", port); +		return NULL; +	} + +	return (uint32_t *)&hcor->or_portsc[port]; +} +  int  ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,  		 int length, struct devrequest *req) @@ -616,11 +629,6 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,  	int port = le16_to_cpu(req->index) & 0xff;  	struct ehci_ctrl *ctrl = dev->controller; -	if (port > CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { -		printf("The request port(%d) is not configured\n", port - 1); -		return -1; -	} -	status_reg = (uint32_t *)&ctrl->hcor->or_portsc[port - 1];  	srclen = 0;  	debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n", @@ -631,6 +639,19 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,  	typeReq = req->request | req->requesttype << 8;  	switch (typeReq) { +	case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): +	case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): +	case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): +		status_reg = ehci_get_portsc_register(ctrl->hcor, port - 1); +		if (!status_reg) +			return -1; +		break; +	default: +		status_reg = NULL; +		break; +	} + +	switch (typeReq) {  	case DeviceRequest | USB_REQ_GET_DESCRIPTOR:  		switch (le16_to_cpu(req->value) >> 8) {  		case USB_DT_DEVICE: @@ -809,21 +830,23 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,  		break;  	case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):  		reg = ehci_readl(status_reg); +		reg &= ~EHCI_PS_CLEAR;  		switch (le16_to_cpu(req->value)) {  		case USB_PORT_FEAT_ENABLE:  			reg &= ~EHCI_PS_PE;  			break;  		case USB_PORT_FEAT_C_ENABLE: -			reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_PE; +			reg |= EHCI_PS_PE;  			break;  		case USB_PORT_FEAT_POWER:  			if (HCS_PPC(ehci_readl(&ctrl->hccr->cr_hcsparams))) -				reg = reg & ~(EHCI_PS_CLEAR | EHCI_PS_PP); +				reg &= ~EHCI_PS_PP; +			break;  		case USB_PORT_FEAT_C_CONNECTION: -			reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_CSC; +			reg |= EHCI_PS_CSC;  			break;  		case USB_PORT_FEAT_OVER_CURRENT: -			reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC; +			reg |= EHCI_PS_OCC;  			break;  		case USB_PORT_FEAT_C_RESET:  			ctrl->portreset &= ~(1 << port); @@ -903,6 +926,9 @@ int usb_lowlevel_init(int index, void **controller)  	qh_list->qh_overlay.qt_token =  			cpu_to_hc32(QT_TOKEN_STATUS(QT_TOKEN_STATUS_HALTED)); +	flush_dcache_range((uint32_t)qh_list, +			   ALIGN_END_ADDR(struct QH, qh_list, 1)); +  	/* Set async. queue head pointer. */  	ehci_writel(&ehcic[index].hcor->or_asynclistaddr, (uint32_t)qh_list); @@ -916,6 +942,9 @@ int usb_lowlevel_init(int index, void **controller)  	periodic->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);  	periodic->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); +	flush_dcache_range((uint32_t)periodic, +			   ALIGN_END_ADDR(struct QH, periodic, 1)); +  	/*  	 * Step 2: Setup frame-list: Every microframe, USB tries the same list.  	 *         In particular, device specifications on polling frequency @@ -933,6 +962,10 @@ int usb_lowlevel_init(int index, void **controller)  						| QH_LINK_TYPE_QH;  	} +	flush_dcache_range((uint32_t)ehcic[index].periodic_list, +			   ALIGN_END_ADDR(uint32_t, ehcic[index].periodic_list, +					  1024)); +  	/* Set periodic list base address */  	ehci_writel(&ehcic[index].hcor->or_periodiclistbase,  		(uint32_t)ehcic[index].periodic_list); @@ -959,10 +992,13 @@ int usb_lowlevel_init(int index, void **controller)  	cmd |= CMD_RUN;  	ehci_writel(&ehcic[index].hcor->or_usbcmd, cmd); +#ifndef CONFIG_USB_EHCI_FARADAY  	/* take control over the ports */  	cmd = ehci_readl(&ehcic[index].hcor->or_configflag);  	cmd |= FLAG_CF;  	ehci_writel(&ehcic[index].hcor->or_configflag, cmd); +#endif +  	/* unblock posted write */  	cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd);  	mdelay(5); @@ -1144,6 +1180,16 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize,  		*buf = buffer + i * elementsize;  	} +	flush_dcache_range((uint32_t)buffer, +			   ALIGN_END_ADDR(char, buffer, +					  queuesize * elementsize)); +	flush_dcache_range((uint32_t)result->first, +			   ALIGN_END_ADDR(struct QH, result->first, +					  queuesize)); +	flush_dcache_range((uint32_t)result->tds, +			   ALIGN_END_ADDR(struct qTD, result->tds, +					  queuesize)); +  	if (disable_periodic(ctrl) < 0) {  		debug("FATAL: periodic should never fail, but did");  		goto fail3; @@ -1154,6 +1200,11 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize,  	result->last->qh_link = list->qh_link;  	list->qh_link = (uint32_t)result->first | QH_LINK_TYPE_QH; +	flush_dcache_range((uint32_t)result->last, +			   ALIGN_END_ADDR(struct QH, result->last, 1)); +	flush_dcache_range((uint32_t)list, +			   ALIGN_END_ADDR(struct QH, list, 1)); +  	if (enable_periodic(ctrl) < 0) {  		debug("FATAL: periodic should never fail, but did");  		goto fail3; @@ -1184,6 +1235,8 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue)  		return NULL;  	}  	/* still active */ +	invalidate_dcache_range((uint32_t)cur, +				ALIGN_END_ADDR(struct QH, cur, 1));  	if (cur->qh_overlay.qt_token & 0x80) {  		debug("Exit poll_int_queue with no completed intr transfer. "  		      "token is %x\n", cur->qh_overlay.qt_token); @@ -1290,6 +1343,9 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,  		return -EINVAL;  	} +	invalidate_dcache_range((uint32_t)buffer, +				ALIGN_END_ADDR(char, buffer, length)); +  	ret = destroy_int_queue(dev, queue);  	if (ret < 0)  		return ret; diff --git a/drivers/video/cfb_console.c b/drivers/video/cfb_console.c index 0793f07f2..b10f1590b 100644 --- a/drivers/video/cfb_console.c +++ b/drivers/video/cfb_console.c @@ -572,8 +572,6 @@ static void video_drawchars(int xx, int yy, unsigned char *s, int count)  					SWAP32((video_font_draw_table32  						[bits & 15][3] & eorx) ^ bgx);  			} -			if (cfb_do_flush_cache) -				flush_cache((ulong)dest0, 32);  			dest0 += VIDEO_FONT_WIDTH * VIDEO_PIXEL_SIZE;  			s++;  		} @@ -642,8 +640,6 @@ static void video_invertchar(int xx, int yy)  		for (x = firstx; x < lastx; x++) {  			u8 *dest = (u8 *)(video_fb_address) + x + y;  			*dest = ~*dest; -			if (cfb_do_flush_cache) -				flush_cache((ulong)dest, 4);  		}  	}  } @@ -687,6 +683,8 @@ void console_cursor(int state)  		}  		cursor_state = state;  	} +	if (cfb_do_flush_cache) +		flush_cache(VIDEO_FB_ADRS, VIDEO_SIZE);  }  #endif @@ -739,8 +737,6 @@ static void console_clear_line(int line, int begin, int end)  			memsetl(offset + i * VIDEO_LINE_LEN, size, bgx);  	}  #endif -	if (cfb_do_flush_cache) -		flush_cache((ulong)CONSOLE_ROW_FIRST, CONSOLE_SIZE);  }  static void console_scrollup(void) @@ -1146,6 +1142,8 @@ void video_putc(const char c)  #else  	parse_putc(c);  #endif +	if (cfb_do_flush_cache) +		flush_cache(VIDEO_FB_ADRS, VIDEO_SIZE);  }  void video_puts(const char *s) @@ -1799,6 +1797,8 @@ int video_display_bitmap(ulong bmp_image, int x, int y)  	}  #endif +	if (cfb_do_flush_cache) +		flush_cache(VIDEO_FB_ADRS, VIDEO_SIZE);  	return (0);  }  #endif @@ -2209,6 +2209,9 @@ static int video_init(void)  	console_col = 0;  	console_row = 0; +	if (cfb_do_flush_cache) +		flush_cache(VIDEO_FB_ADRS, VIDEO_SIZE); +  	return 0;  } |