From patchwork Mon Dec 10 15:24:32 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pantelis Antoniou X-Patchwork-Id: 204940 X-Patchwork-Delegate: scottwood@freescale.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 75C292C0315 for ; Tue, 11 Dec 2012 02:24:56 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id CF3DB4A094; Mon, 10 Dec 2012 16:24:54 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id mpCt7kiGNYbj; Mon, 10 Dec 2012 16:24:54 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 2F75A4A050; Mon, 10 Dec 2012 16:24:50 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 1AEC34A050 for ; Mon, 10 Dec 2012 16:24:47 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id ke+T3T0kpT8u for ; Mon, 10 Dec 2012 16:24:44 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from li42-95.members.linode.com (li42-95.members.linode.com [209.123.162.95]) by theia.denx.de (Postfix) with ESMTPS id C48814A057 for ; Mon, 10 Dec 2012 16:24:36 +0100 (CET) Received: from sles11esa.localdomain (unknown [195.97.110.117]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) (Authenticated sender: panto) by li42-95.members.linode.com (Postfix) with ESMTPSA id B17FF9C10A; Mon, 10 Dec 2012 15:24:33 +0000 (UTC) From: Pantelis Antoniou To: Marek Vasut Date: Mon, 10 Dec 2012 17:24:32 +0200 Message-Id: <1355153072-6047-1-git-send-email-panto@antoniou-consulting.com> X-Mailer: git-send-email 1.7.12 Cc: Scott Wood , u-boot@lists.denx.de, Tom Rini , Pantelis Antoniou Subject: [U-Boot] [PATCH] [DFU] Implement NAND dfu support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de Introduce on-the fly DFU NAND support. Signed-off-by: Pantelis Antoniou --- drivers/dfu/Makefile | 1 + drivers/dfu/dfu.c | 7 ++ drivers/dfu/dfu_nand.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++ include/dfu.h | 23 ++++++ 4 files changed, 225 insertions(+) create mode 100644 drivers/dfu/dfu_nand.c diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index 7b717bc..153095d 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 fb9b417..1972b17 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -234,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; } @@ -263,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; } @@ -285,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 0000000..9ea5f0c --- /dev/null +++ b/drivers/dfu/dfu_nand.c @@ -0,0 +1,194 @@ +/* + * dfu_nand.c -- DFU for NAND routines. + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * Based on dfu_mmc.c which is: + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +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) +{ + char cmd_buf[DFU_CMD_BUF_SIZE]; + u64 start, count; + int ret; + int dev; + loff_t actual; + + /* 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; + count = *len; + if (start + count > + dfu->data.nand.start + dfu->data.nand.size) { + printf("%s: block_op out of bounds\n", __func__); + return -1; + } + dev = nand_curr_device; + if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || + !nand_info[dev].name) { + printf("%s: invalid nand device\n", __func__); + return -1; + } + + sprintf(cmd_buf, "nand %s %p %llx %llx", + op == DFU_OP_READ ? "read" : "write", + buf, start, count); + + debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); + ret = run_command(cmd_buf, 0); + + /* find out how much actual bytes have been written */ + /* the difference is the amount of skip we must add from now on */ + actual = nand_extent_skip_bad(&nand_info[dev], start, count); + if (actual == (loff_t)-1) { + printf("nand_extend_skip_bad: error!\n"); + return ret; + } + + if (actual > (start + count)) { + debug("%s: skipped %llx bad bytes at %llx\n", __func__, + actual - (start + count), start); + dfu->bad_skip += (u32)(actual - (start + 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; +} + +extern int mtdparts_init(void); +extern struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part_num); +extern int find_dev_and_part(const char *id, struct mtd_device **dev, + u8 *part_num, struct part_info **part); + + +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 2847ef8..0454762 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; @@ -71,6 +80,7 @@ struct dfu_entity { union { struct mmc_internal_data mmc; + struct nand_internal_data nand; } data; int (*read_medium)(struct dfu_entity *dfu, @@ -91,6 +101,8 @@ struct dfu_entity { long r_left; long b_left; + u32 bad_skip; /* for nand use */ + unsigned int inited : 1; }; @@ -115,4 +127,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_ */