diff options
| -rw-r--r-- | README | 3 | ||||
| -rw-r--r-- | drivers/dfu/Makefile | 1 | ||||
| -rw-r--r-- | drivers/dfu/dfu.c | 8 | ||||
| -rw-r--r-- | drivers/dfu/dfu_nand.c | 187 | ||||
| -rw-r--r-- | include/dfu.h | 23 | 
5 files changed, 222 insertions, 0 deletions
| @@ -1357,6 +1357,9 @@ The following options need to be configured:  		CONFIG_DFU_MMC  		This enables support for exposing (e)MMC devices via DFU. +		CONFIG_DFU_NAND +		This enables support for exposing NAND devices via DFU. +  		CONFIG_SYS_DFU_MAX_FILE_SIZE  		When updating files rather than the raw storage device,  		we use a static buffer to copy the file into and then write diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index 7b717bce2..153095d71 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -27,6 +27,7 @@ LIB	= $(obj)libdfu.o  COBJS-$(CONFIG_DFU_FUNCTION) += dfu.o  COBJS-$(CONFIG_DFU_MMC) += dfu_mmc.o +COBJS-$(CONFIG_DFU_NAND) += dfu_nand.o  SRCS    := $(COBJS-y:.o=.c)  OBJS	:= $(addprefix $(obj),$(COBJS-y)) diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index 609061dfa..6af6890d0 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -85,6 +85,7 @@ int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)  		/* initial state */  		dfu->crc = 0;  		dfu->offset = 0; +		dfu->bad_skip = 0;  		dfu->i_blk_seq_num = 0;  		dfu->i_buf_start = dfu_buf;  		dfu->i_buf_end = dfu_buf + sizeof(dfu_buf); @@ -233,6 +234,8 @@ int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)  		dfu->i_buf = dfu->i_buf_start;  		dfu->b_left = 0; +		dfu->bad_skip = 0; +  		dfu->inited = 1;  	} @@ -262,6 +265,8 @@ int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)  		dfu->i_buf = dfu->i_buf_start;  		dfu->b_left = 0; +		dfu->bad_skip = 0; +  		dfu->inited = 0;  	} @@ -284,6 +289,9 @@ static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt,  	if (strcmp(interface, "mmc") == 0) {  		if (dfu_fill_entity_mmc(dfu, s))  			return -1; +	} else if (strcmp(interface, "nand") == 0) { +		if (dfu_fill_entity_nand(dfu, s)) +			return -1;  	} else {  		printf("%s: Device %s not (yet) supported!\n",  		       __func__,  interface); diff --git a/drivers/dfu/dfu_nand.c b/drivers/dfu/dfu_nand.c new file mode 100644 index 000000000..7dc89b2f2 --- /dev/null +++ b/drivers/dfu/dfu_nand.c @@ -0,0 +1,187 @@ +/* + * dfu_nand.c -- DFU for NAND routines. + * + * Copyright (C) 2012-2013 Texas Instruments, Inc. + * + * Based on dfu_mmc.c which is: + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski <l.majewski@samsung.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 <malloc.h> +#include <errno.h> +#include <div64.h> +#include <dfu.h> +#include <linux/mtd/mtd.h> +#include <jffs2/load_kernel.h> +#include <nand.h> + +enum dfu_nand_op { +	DFU_OP_READ = 1, +	DFU_OP_WRITE, +}; + +static int nand_block_op(enum dfu_nand_op op, struct dfu_entity *dfu, +			u64 offset, void *buf, long *len) +{ +	loff_t start, lim; +	size_t count, actual; +	int ret; +	nand_info_t *nand; + +	/* if buf == NULL return total size of the area */ +	if (buf == NULL) { +		*len = dfu->data.nand.size; +		return 0; +	} + +	start = dfu->data.nand.start + offset + dfu->bad_skip; +	lim = dfu->data.nand.start + dfu->data.nand.size - start; +	count = *len; + +	if (nand_curr_device < 0 || +	    nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || +	    !nand_info[nand_curr_device].name) { +		printf("%s: invalid nand device\n", __func__); +		return -1; +	} + +	nand = &nand_info[nand_curr_device]; + +	if (op == DFU_OP_READ) +		ret = nand_read_skip_bad(nand, start, &count, &actual, +				lim, buf); +	else +		ret = nand_write_skip_bad(nand, start, &count, &actual, +				lim, buf, 0); + +	if (ret != 0) { +		printf("%s: nand_%s_skip_bad call failed at %llx!\n", +		       __func__, op == DFU_OP_READ ? "read" : "write", +		       start); +		return ret; +	} + +	/* +	 * Find out where we stopped writing data.  This can be deeper into +	 * the NAND than we expected due to having to skip bad blocks.  So +	 * we must take this into account for the next write, if any. +	 */ +	if (actual > count) +		dfu->bad_skip += actual - count; + +	return ret; +} + +static inline int nand_block_write(struct dfu_entity *dfu, +		u64 offset, void *buf, long *len) +{ +	return nand_block_op(DFU_OP_WRITE, dfu, offset, buf, len); +} + +static inline int nand_block_read(struct dfu_entity *dfu, +		u64 offset, void *buf, long *len) +{ +	return nand_block_op(DFU_OP_READ, dfu, offset, buf, len); +} + +static int dfu_write_medium_nand(struct dfu_entity *dfu, +		u64 offset, void *buf, long *len) +{ +	int ret = -1; + +	switch (dfu->layout) { +	case DFU_RAW_ADDR: +		ret = nand_block_write(dfu, offset, buf, len); +		break; +	default: +		printf("%s: Layout (%s) not (yet) supported!\n", __func__, +		       dfu_get_layout(dfu->layout)); +	} + +	return ret; +} + +static int dfu_read_medium_nand(struct dfu_entity *dfu, u64 offset, void *buf, +		long *len) +{ +	int ret = -1; + +	switch (dfu->layout) { +	case DFU_RAW_ADDR: +		ret = nand_block_read(dfu, offset, buf, len); +		break; +	default: +		printf("%s: Layout (%s) not (yet) supported!\n", __func__, +		       dfu_get_layout(dfu->layout)); +	} + +	return ret; +} + +int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s) +{ +	char *st; +	int ret, dev, part; + +	dfu->dev_type = DFU_DEV_NAND; +	st = strsep(&s, " "); +	if (!strcmp(st, "raw")) { +		dfu->layout = DFU_RAW_ADDR; +		dfu->data.nand.start = simple_strtoul(s, &s, 16); +		s++; +		dfu->data.nand.size = simple_strtoul(s, &s, 16); +	} else if (!strcmp(st, "part")) { +		char mtd_id[32]; +		struct mtd_device *mtd_dev; +		u8 part_num; +		struct part_info *pi; + +		dfu->layout = DFU_RAW_ADDR; + +		dev = simple_strtoul(s, &s, 10); +		s++; +		part = simple_strtoul(s, &s, 10); + +		sprintf(mtd_id, "%s%d,%d", "nand", dev, part - 1); +		printf("using id '%s'\n", mtd_id); + +		mtdparts_init(); + +		ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi); +		if (ret != 0) { +			printf("Could not locate '%s'\n", mtd_id); +			return -1; +		} + +		dfu->data.nand.start = pi->offset; +		dfu->data.nand.size = pi->size; + +	} else { +		printf("%s: Memory layout (%s) not supported!\n", __func__, st); +		return -1; +	} + +	dfu->read_medium = dfu_read_medium_nand; +	dfu->write_medium = dfu_write_medium_nand; + +	/* initial state */ +	dfu->inited = 0; + +	return 0; +} diff --git a/include/dfu.h b/include/dfu.h index cc9926861..a107f4b13 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -52,6 +52,15 @@ struct mmc_internal_data {  	unsigned int part;  }; +struct nand_internal_data { +	/* RAW programming */ +	u64 start; +	u64 size; + +	unsigned int dev; +	unsigned int part; +}; +  static inline unsigned int get_mmc_blk_size(int dev)  {  	return find_mmc_device(dev)->read_bl_len; @@ -74,6 +83,7 @@ struct dfu_entity {  	union {  		struct mmc_internal_data mmc; +		struct nand_internal_data nand;  	} data;  	int (*read_medium)(struct dfu_entity *dfu, @@ -96,6 +106,8 @@ struct dfu_entity {  	long r_left;  	long b_left; +	u32 bad_skip;	/* for nand use */ +  	unsigned int inited:1;  }; @@ -120,4 +132,15 @@ static inline int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)  	return -1;  }  #endif + +#ifdef CONFIG_DFU_NAND +extern int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s); +#else +static inline int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s) +{ +	puts("NAND support not available!\n"); +	return -1; +} +#endif +  #endif /* __DFU_ENTITY_H_ */ |