Support for NAND storage devices to work with the DFU framework. Signed-off-by: Pantelis Antoniou <panto@antoniou-consulting.com> Signed-off-by: Tom Rini <trini@ti.com> Acked-by: Scott Wood <scottwood@freescale.com>master
parent
c4df2f4100
commit
c6631764c2
@ -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; |
||||||
|
} |
Loading…
Reference in new issue