Merge branch 'master' of git://git.denx.de/u-boot-usb

This commit is contained in:
Tom Rini 2013-04-11 14:31:50 -04:00
commit 980464a4fc
13 changed files with 690 additions and 138 deletions

23
README
View File

@ -1344,6 +1344,29 @@ The following options need to be configured:
CONFIG_SH_MMCIF_CLK
Define the clock frequency for MMCIF
- USB Device Firmware Update (DFU) class support:
CONFIG_DFU_FUNCTION
This enables the USB portion of the DFU USB class
CONFIG_CMD_DFU
This enables the command "dfu" which is used to have
U-Boot create a DFU class device via USB. This command
requires that the "dfu_alt_info" environment variable be
set and define the alt settings to expose to the host.
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
the buffer once we've been given the whole file. Define
this to the maximum filesize (in bytes) for the buffer.
Default is 4 MiB if undefined.
- Journaling Flash filesystem support:
CONFIG_JFFS2_NAND, CONFIG_JFFS2_NAND_OFF, CONFIG_JFFS2_NAND_SIZE,
CONFIG_JFFS2_NAND_DEV

View File

@ -167,6 +167,11 @@ void fdt_fixup_dr_usb(void *blob, bd_t *bd)
}
}
if (mode_idx < 0 || phy_idx < 0) {
puts("ERROR: wrong usb mode/phy defined!!\n");
return;
}
dr_mode_type = modes[mode_idx];
dr_phy_type = phys[phy_idx];

View File

@ -91,6 +91,7 @@ static int splash_load_from_nand(u32 bmp_load_addr)
res = nand_read_skip_bad(&nand_info[nand_curr_device],
splash_screen_nand_offset, &bmp_header_size,
NULL, nand_info[nand_curr_device].size,
(u_char *)bmp_load_addr);
if (res < 0)
return res;
@ -103,6 +104,7 @@ static int splash_load_from_nand(u32 bmp_load_addr)
return nand_read_skip_bad(&nand_info[nand_curr_device],
splash_screen_nand_offset, &bmp_size,
NULL, nand_info[nand_curr_device].size,
(u_char *)bmp_load_addr);
splash_address_too_high:

View File

@ -137,7 +137,8 @@ static inline int str2long(const char *p, ulong *num)
return *p != '\0' && *endptr == '\0';
}
static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size)
static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size,
loff_t *maxsize)
{
#ifdef CONFIG_CMD_MTDPARTS
struct mtd_device *dev;
@ -160,6 +161,7 @@ static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size)
*off = part->offset;
*size = part->size;
*maxsize = part->size;
*idx = dev->id->num;
ret = set_dev(*idx);
@ -173,10 +175,11 @@ static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size)
#endif
}
static int arg_off(const char *arg, int *idx, loff_t *off, loff_t *maxsize)
static int arg_off(const char *arg, int *idx, loff_t *off, loff_t *size,
loff_t *maxsize)
{
if (!str2off(arg, off))
return get_part(arg, idx, off, maxsize);
return get_part(arg, idx, off, size, maxsize);
if (*off >= nand_info[*idx].size) {
puts("Offset exceeds device limit\n");
@ -184,36 +187,35 @@ static int arg_off(const char *arg, int *idx, loff_t *off, loff_t *maxsize)
}
*maxsize = nand_info[*idx].size - *off;
*size = *maxsize;
return 0;
}
static int arg_off_size(int argc, char *const argv[], int *idx,
loff_t *off, loff_t *size)
loff_t *off, loff_t *size, loff_t *maxsize)
{
int ret;
loff_t maxsize = 0;
if (argc == 0) {
*off = 0;
*size = nand_info[*idx].size;
*maxsize = *size;
goto print;
}
ret = arg_off(argv[0], idx, off, &maxsize);
ret = arg_off(argv[0], idx, off, size, maxsize);
if (ret)
return ret;
if (argc == 1) {
*size = maxsize;
if (argc == 1)
goto print;
}
if (!str2off(argv[1], size)) {
printf("'%s' is not a number\n", argv[1]);
return -1;
}
if (*size > maxsize) {
if (*size > *maxsize) {
puts("Size exceeds partition or device limit\n");
return -1;
}
@ -307,7 +309,8 @@ int do_nand_env_oob(cmd_tbl_t *cmdtp, int argc, char *const argv[])
if (argc < 3)
goto usage;
if (arg_off(argv[2], &idx, &addr, &maxsize)) {
/* We don't care about size, or maxsize. */
if (arg_off(argv[2], &idx, &addr, &maxsize, &maxsize)) {
puts("Offset or partition name expected\n");
return 1;
}
@ -426,7 +429,7 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int i, ret = 0;
ulong addr;
loff_t off, size;
loff_t off, size, maxsize;
char *cmd, *s;
nand_info_t *nand;
#ifdef CONFIG_SYS_NAND_QUIET
@ -551,7 +554,8 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
printf("\nNAND %s: ", cmd);
/* skip first two or three arguments, look for offset and size */
if (arg_off_size(argc - o, argv + o, &dev, &off, &size) != 0)
if (arg_off_size(argc - o, argv + o, &dev, &off, &size,
&maxsize) != 0)
return 1;
nand = &nand_info[dev];
@ -619,7 +623,7 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
if (s && !strcmp(s, ".raw")) {
raw = 1;
if (arg_off(argv[3], &dev, &off, &size))
if (arg_off(argv[3], &dev, &off, &size, &maxsize))
return 1;
if (argc > 4 && !str2long(argv[4], &pagecount)) {
@ -635,7 +639,7 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
rwsize = pagecount * (nand->writesize + nand->oobsize);
} else {
if (arg_off_size(argc - 3, argv + 3, &dev,
&off, &size) != 0)
&off, &size, &maxsize) != 0)
return 1;
rwsize = size;
@ -645,9 +649,11 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
!strcmp(s, ".e") || !strcmp(s, ".i")) {
if (read)
ret = nand_read_skip_bad(nand, off, &rwsize,
NULL, maxsize,
(u_char *)addr);
else
ret = nand_write_skip_bad(nand, off, &rwsize,
NULL, maxsize,
(u_char *)addr, 0);
#ifdef CONFIG_CMD_NAND_TRIMFFS
} else if (!strcmp(s, ".trimffs")) {
@ -655,8 +661,8 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
printf("Unknown nand command suffix '%s'\n", s);
return 1;
}
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr,
ret = nand_write_skip_bad(nand, off, &rwsize, NULL,
maxsize, (u_char *)addr,
WITH_DROP_FFS);
#endif
#ifdef CONFIG_CMD_NAND_YAFFS
@ -665,9 +671,9 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
printf("Unknown nand command suffix '%s'.\n", s);
return 1;
}
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr,
WITH_INLINE_OOB);
ret = nand_write_skip_bad(nand, off, &rwsize, NULL,
maxsize, (u_char *)addr,
WITH_YAFFS_OOB);
#endif
} else if (!strcmp(s, ".oob")) {
/* out-of-band data */
@ -775,7 +781,8 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
if (s && !strcmp(s, ".allexcept"))
allexcept = 1;
if (arg_off_size(argc - 2, argv + 2, &dev, &off, &size) < 0)
if (arg_off_size(argc - 2, argv + 2, &dev, &off, &size,
&maxsize) < 0)
return 1;
if (!nand_unlock(&nand_info[dev], off, size, allexcept)) {
@ -873,7 +880,8 @@ static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand,
printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset);
cnt = nand->writesize;
r = nand_read_skip_bad(nand, offset, &cnt, (u_char *) addr);
r = nand_read_skip_bad(nand, offset, &cnt, NULL, nand->size,
(u_char *)addr);
if (r) {
puts("** Read error\n");
bootstage_error(BOOTSTAGE_ID_NAND_HDR_READ);
@ -905,7 +913,8 @@ static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand,
}
bootstage_mark(BOOTSTAGE_ID_NAND_TYPE);
r = nand_read_skip_bad(nand, offset, &cnt, (u_char *) addr);
r = nand_read_skip_bad(nand, offset, &cnt, NULL, nand->size,
(u_char *)addr);
if (r) {
puts("** Read error\n");
bootstage_error(BOOTSTAGE_ID_NAND_READ);

View File

@ -281,7 +281,8 @@ int readenv(size_t offset, u_char *buf)
} else {
char_ptr = &buf[amount_loaded];
if (nand_read_skip_bad(&nand_info[0], offset,
&len, char_ptr))
&len, NULL,
nand_info[0].size, char_ptr))
return 1;
offset += blocksize;

View File

@ -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))

View File

@ -44,90 +44,232 @@ static int dfu_find_alt_num(const char *s)
static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
dfu_buf[DFU_DATA_BUF_SIZE];
int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
static int dfu_write_buffer_drain(struct dfu_entity *dfu)
{
static unsigned char *i_buf;
static int i_blk_seq_num;
long w_size = 0;
int ret = 0;
long w_size;
int ret;
debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n",
__func__, dfu->name, buf, size, blk_seq_num, i_buf);
/* flush size? */
w_size = dfu->i_buf - dfu->i_buf_start;
if (w_size == 0)
return 0;
if (blk_seq_num == 0) {
i_buf = dfu_buf;
i_blk_seq_num = 0;
}
/* update CRC32 */
dfu->crc = crc32(dfu->crc, dfu->i_buf_start, w_size);
if (i_blk_seq_num++ != blk_seq_num) {
printf("%s: Wrong sequence number! [%d] [%d]\n",
__func__, i_blk_seq_num, blk_seq_num);
return -1;
}
ret = dfu->write_medium(dfu, dfu->offset, dfu->i_buf_start, &w_size);
if (ret)
debug("%s: Write error!\n", __func__);
memcpy(i_buf, buf, size);
i_buf += size;
/* point back */
dfu->i_buf = dfu->i_buf_start;
if (size == 0) {
/* Integrity check (if needed) */
debug("%s: %s %d [B] CRC32: 0x%x\n", __func__, dfu->name,
i_buf - dfu_buf, crc32(0, dfu_buf, i_buf - dfu_buf));
/* update offset */
dfu->offset += w_size;
w_size = i_buf - dfu_buf;
ret = dfu->write_medium(dfu, dfu_buf, &w_size);
if (ret)
debug("%s: Write error!\n", __func__);
i_blk_seq_num = 0;
i_buf = NULL;
return ret;
}
puts("#");
return ret;
}
int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
{
static unsigned char *i_buf;
static int i_blk_seq_num;
static long r_size;
static u32 crc;
int ret = 0;
int tret;
debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n",
__func__, dfu->name, buf, size, blk_seq_num, i_buf);
debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x offset: 0x%llx bufoffset: 0x%x\n",
__func__, dfu->name, buf, size, blk_seq_num, dfu->offset,
dfu->i_buf - dfu->i_buf_start);
if (blk_seq_num == 0) {
i_buf = dfu_buf;
ret = dfu->read_medium(dfu, i_buf, &r_size);
debug("%s: %s %ld [B]\n", __func__, dfu->name, r_size);
i_blk_seq_num = 0;
/* Integrity check (if needed) */
crc = crc32(0, dfu_buf, r_size);
if (!dfu->inited) {
/* 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);
dfu->i_buf = dfu->i_buf_start;
dfu->inited = 1;
}
if (i_blk_seq_num++ != blk_seq_num) {
if (dfu->i_blk_seq_num != blk_seq_num) {
printf("%s: Wrong sequence number! [%d] [%d]\n",
__func__, i_blk_seq_num, blk_seq_num);
__func__, dfu->i_blk_seq_num, blk_seq_num);
return -1;
}
if (r_size >= size) {
memcpy(buf, i_buf, size);
i_buf += size;
r_size -= size;
return size;
} else {
memcpy(buf, i_buf, r_size);
i_buf += r_size;
debug("%s: %s CRC32: 0x%x\n", __func__, dfu->name, crc);
puts("UPLOAD ... done\nCtrl+C to exit ...\n");
/* DFU 1.1 standard says:
* The wBlockNum field is a block sequence number. It increments each
* time a block is transferred, wrapping to zero from 65,535. It is used
* to provide useful context to the DFU loader in the device."
*
* This means that it's a 16 bit counter that roll-overs at
* 0xffff -> 0x0000. By having a typical 4K transfer block
* we roll-over at exactly 256MB. Not very fun to debug.
*
* Handling rollover, and having an inited variable,
* makes things work.
*/
i_buf = NULL;
i_blk_seq_num = 0;
crc = 0;
return r_size;
/* handle rollover */
dfu->i_blk_seq_num = (dfu->i_blk_seq_num + 1) & 0xffff;
/* flush buffer if overflow */
if ((dfu->i_buf + size) > dfu->i_buf_end) {
tret = dfu_write_buffer_drain(dfu);
if (ret == 0)
ret = tret;
}
/* we should be in buffer now (if not then size too large) */
if ((dfu->i_buf + size) > dfu->i_buf_end) {
printf("%s: Wrong size! [%d] [%d] - %d\n",
__func__, dfu->i_blk_seq_num, blk_seq_num, size);
return -1;
}
memcpy(dfu->i_buf, buf, size);
dfu->i_buf += size;
/* if end or if buffer full flush */
if (size == 0 || (dfu->i_buf + size) > dfu->i_buf_end) {
tret = dfu_write_buffer_drain(dfu);
if (ret == 0)
ret = tret;
}
/* end? */
if (size == 0) {
/* Now try and flush to the medium if needed. */
if (dfu->flush_medium)
ret = dfu->flush_medium(dfu);
printf("\nDFU complete CRC32: 0x%08x\n", dfu->crc);
/* clear everything */
dfu->crc = 0;
dfu->offset = 0;
dfu->i_blk_seq_num = 0;
dfu->i_buf_start = dfu_buf;
dfu->i_buf_end = dfu_buf + sizeof(dfu_buf);
dfu->i_buf = dfu->i_buf_start;
dfu->inited = 0;
}
return ret = 0 ? size : ret;
}
static int dfu_read_buffer_fill(struct dfu_entity *dfu, void *buf, int size)
{
long chunk;
int ret, readn;
readn = 0;
while (size > 0) {
/* get chunk that can be read */
chunk = min(size, dfu->b_left);
/* consume */
if (chunk > 0) {
memcpy(buf, dfu->i_buf, chunk);
dfu->crc = crc32(dfu->crc, buf, chunk);
dfu->i_buf += chunk;
dfu->b_left -= chunk;
size -= chunk;
buf += chunk;
readn += chunk;
}
/* all done */
if (size > 0) {
/* no more to read */
if (dfu->r_left == 0)
break;
dfu->i_buf = dfu->i_buf_start;
dfu->b_left = dfu->i_buf_end - dfu->i_buf_start;
/* got to read, but buffer is empty */
if (dfu->b_left > dfu->r_left)
dfu->b_left = dfu->r_left;
ret = dfu->read_medium(dfu, dfu->offset, dfu->i_buf,
&dfu->b_left);
if (ret != 0) {
debug("%s: Read error!\n", __func__);
return ret;
}
dfu->offset += dfu->b_left;
dfu->r_left -= dfu->b_left;
puts("#");
}
}
return readn;
}
int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
{
int ret = 0;
debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n",
__func__, dfu->name, buf, size, blk_seq_num, dfu->i_buf);
if (!dfu->inited) {
ret = dfu->read_medium(dfu, 0, buf, &dfu->r_left);
if (ret != 0) {
debug("%s: failed to get r_left\n", __func__);
return ret;
}
debug("%s: %s %ld [B]\n", __func__, dfu->name, dfu->r_left);
dfu->i_blk_seq_num = 0;
dfu->crc = 0;
dfu->offset = 0;
dfu->i_buf_start = dfu_buf;
dfu->i_buf_end = dfu_buf + sizeof(dfu_buf);
dfu->i_buf = dfu->i_buf_start;
dfu->b_left = 0;
dfu->bad_skip = 0;
dfu->inited = 1;
}
if (dfu->i_blk_seq_num != blk_seq_num) {
printf("%s: Wrong sequence number! [%d] [%d]\n",
__func__, dfu->i_blk_seq_num, blk_seq_num);
return -1;
}
/* handle rollover */
dfu->i_blk_seq_num = (dfu->i_blk_seq_num + 1) & 0xffff;
ret = dfu_read_buffer_fill(dfu, buf, size);
if (ret < 0) {
printf("%s: Failed to fill buffer\n", __func__);
return -1;
}
if (ret < size) {
debug("%s: %s CRC32: 0x%x\n", __func__, dfu->name, dfu->crc);
puts("\nUPLOAD ... done\nCtrl+C to exit ...\n");
dfu->i_blk_seq_num = 0;
dfu->crc = 0;
dfu->offset = 0;
dfu->i_buf_start = dfu_buf;
dfu->i_buf_end = dfu_buf + sizeof(dfu_buf);
dfu->i_buf = dfu->i_buf_start;
dfu->b_left = 0;
dfu->bad_skip = 0;
dfu->inited = 0;
}
return ret;
}
@ -147,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);

View File

@ -22,6 +22,7 @@
#include <common.h>
#include <malloc.h>
#include <errno.h>
#include <div64.h>
#include <dfu.h>
enum dfu_mmc_op {
@ -29,32 +30,51 @@ enum dfu_mmc_op {
DFU_OP_WRITE,
};
static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE];
static long dfu_file_buf_len;
static int mmc_block_op(enum dfu_mmc_op op, struct dfu_entity *dfu,
void *buf, long *len)
u64 offset, void *buf, long *len)
{
char cmd_buf[DFU_CMD_BUF_SIZE];
u32 blk_start, blk_count;
sprintf(cmd_buf, "mmc %s 0x%x %x %x",
/*
* We must ensure that we work in lba_blk_size chunks, so ALIGN
* this value.
*/
*len = ALIGN(*len, dfu->data.mmc.lba_blk_size);
blk_start = dfu->data.mmc.lba_start +
(u32)lldiv(offset, dfu->data.mmc.lba_blk_size);
blk_count = *len / dfu->data.mmc.lba_blk_size;
if (blk_start + blk_count >
dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) {
puts("Request would exceed designated area!\n");
return -EINVAL;
}
sprintf(cmd_buf, "mmc %s %p %x %x",
op == DFU_OP_READ ? "read" : "write",
(unsigned int) buf,
dfu->data.mmc.lba_start,
dfu->data.mmc.lba_size);
if (op == DFU_OP_READ)
*len = dfu->data.mmc.lba_blk_size * dfu->data.mmc.lba_size;
buf, blk_start, blk_count);
debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
return run_command(cmd_buf, 0);
}
static inline int mmc_block_write(struct dfu_entity *dfu, void *buf, long *len)
static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len)
{
return mmc_block_op(DFU_OP_WRITE, dfu, buf, len);
}
if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) {
dfu_file_buf_len = 0;
return -EINVAL;
}
static inline int mmc_block_read(struct dfu_entity *dfu, void *buf, long *len)
{
return mmc_block_op(DFU_OP_READ, dfu, buf, len);
/* Add to the current buffer. */
memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len);
dfu_file_buf_len += *len;
return 0;
}
static int mmc_file_op(enum dfu_mmc_op op, struct dfu_entity *dfu,
@ -66,20 +86,23 @@ static int mmc_file_op(enum dfu_mmc_op op, struct dfu_entity *dfu,
switch (dfu->layout) {
case DFU_FS_FAT:
sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s %lx",
sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s",
op == DFU_OP_READ ? "load" : "write",
dfu->data.mmc.dev, dfu->data.mmc.part,
(unsigned int) buf, dfu->name, *len);
(unsigned int) buf, dfu->name);
if (op == DFU_OP_WRITE)
sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len);
break;
case DFU_FS_EXT4:
sprintf(cmd_buf, "ext4%s mmc %d:%d 0x%x /%s %ld",
sprintf(cmd_buf, "ext4%s mmc %d:%d 0x%x /%s",
op == DFU_OP_READ ? "load" : "write",
dfu->data.mmc.dev, dfu->data.mmc.part,
(unsigned int) buf, dfu->name, *len);
(unsigned int) buf, dfu->name);
break;
default:
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
dfu_get_layout(dfu->layout));
return -1;
}
debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
@ -102,27 +125,18 @@ static int mmc_file_op(enum dfu_mmc_op op, struct dfu_entity *dfu,
return ret;
}
static inline int mmc_file_write(struct dfu_entity *dfu, void *buf, long *len)
{
return mmc_file_op(DFU_OP_WRITE, dfu, buf, len);
}
static inline int mmc_file_read(struct dfu_entity *dfu, void *buf, long *len)
{
return mmc_file_op(DFU_OP_READ, dfu, buf, len);
}
int dfu_write_medium_mmc(struct dfu_entity *dfu, void *buf, long *len)
int dfu_write_medium_mmc(struct dfu_entity *dfu,
u64 offset, void *buf, long *len)
{
int ret = -1;
switch (dfu->layout) {
case DFU_RAW_ADDR:
ret = mmc_block_write(dfu, buf, len);
ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
break;
case DFU_FS_FAT:
case DFU_FS_EXT4:
ret = mmc_file_write(dfu, buf, len);
ret = mmc_file_buffer(dfu, buf, len);
break;
default:
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
@ -132,17 +146,34 @@ int dfu_write_medium_mmc(struct dfu_entity *dfu, void *buf, long *len)
return ret;
}
int dfu_read_medium_mmc(struct dfu_entity *dfu, void *buf, long *len)
int dfu_flush_medium_mmc(struct dfu_entity *dfu)
{
int ret = 0;
if (dfu->layout != DFU_RAW_ADDR) {
/* Do stuff here. */
ret = mmc_file_op(DFU_OP_WRITE, dfu, &dfu_file_buf,
&dfu_file_buf_len);
/* Now that we're done */
dfu_file_buf_len = 0;
}
return ret;
}
int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf,
long *len)
{
int ret = -1;
switch (dfu->layout) {
case DFU_RAW_ADDR:
ret = mmc_block_read(dfu, buf, len);
ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len);
break;
case DFU_FS_FAT:
case DFU_FS_EXT4:
ret = mmc_file_read(dfu, buf, len);
ret = mmc_file_op(DFU_OP_READ, dfu, buf, len);
break;
default:
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
@ -181,14 +212,15 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)
mmc = find_mmc_device(dev);
if (mmc == NULL || mmc_init(mmc)) {
printf("%s: could not find mmc device #%d!\n", __func__, dev);
printf("%s: could not find mmc device #%d!\n",
__func__, dev);
return -ENODEV;
}
blk_dev = &mmc->block_dev;
if (get_partition_info(blk_dev, part, &partinfo) != 0) {
printf("%s: could not find partition #%d on mmc device #%d!\n",
__func__, part, dev);
__func__, part, dev);
return -ENODEV;
}
@ -208,6 +240,10 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)
dfu->read_medium = dfu_read_medium_mmc;
dfu->write_medium = dfu_write_medium_mmc;
dfu->flush_medium = dfu_flush_medium_mmc;
/* initial state */
dfu->inited = 0;
return 0;
}

187
drivers/dfu/dfu_nand.c Normal file
View File

@ -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;
}

View File

@ -416,11 +416,13 @@ int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length,
* @param nand NAND device
* @param offset offset in flash
* @param length image length
* @param used length of flash needed for the requested length
* @return 0 if the image fits and there are no bad blocks
* 1 if the image fits, but there are bad blocks
* -1 if the image does not fit
*/
static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length)
static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length,
size_t *used)
{
size_t len_excl_bad = 0;
int ret = 0;
@ -442,8 +444,13 @@ static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length)
ret = 1;
offset += block_len;
*used += block_len;
}
/* If the length is not a multiple of block_len, adjust. */
if (len_excl_bad > length)
*used -= (len_excl_bad - length);
return ret;
}
@ -476,23 +483,36 @@ static size_t drop_ffs(const nand_info_t *nand, const u_char *buf,
* Write image to NAND flash.
* Blocks that are marked bad are skipped and the is written to the next
* block instead as long as the image is short enough to fit even after
* skipping the bad blocks.
* skipping the bad blocks. Due to bad blocks we may not be able to
* perform the requested write. In the case where the write would
* extend beyond the end of the NAND device, both length and actual (if
* not NULL) are set to 0. In the case where the write would extend
* beyond the limit we are passed, length is set to 0 and actual is set
* to the required length.
*
* @param nand NAND device
* @param offset offset in flash
* @param length buffer length
* @param actual set to size required to write length worth of
* buffer or 0 on error, if not NULL
* @param lim maximum size that actual may be in order to not
* exceed the buffer
* @param buffer buffer to read from
* @param flags flags modifying the behaviour of the write to NAND
* @return 0 in case of success
*/
int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
u_char *buffer, int flags)
size_t *actual, loff_t lim, u_char *buffer, int flags)
{
int rval = 0, blocksize;
size_t left_to_write = *length;
size_t used_for_write = 0;
u_char *p_buffer = buffer;
int need_skip;
if (actual)
*actual = 0;
#ifdef CONFIG_CMD_NAND_YAFFS
if (flags & WITH_YAFFS_OOB) {
if (flags & ~WITH_YAFFS_OOB)
@ -529,13 +549,23 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
return -EINVAL;
}
need_skip = check_skip_len(nand, offset, *length);
need_skip = check_skip_len(nand, offset, *length, &used_for_write);
if (actual)
*actual = used_for_write;
if (need_skip < 0) {
printf("Attempt to write outside the flash area\n");
*length = 0;
return -EINVAL;
}
if (used_for_write > lim) {
puts("Size of write exceeds partition or device limit\n");
*length = 0;
return -EFBIG;
}
if (!need_skip && !(flags & WITH_DROP_FFS)) {
rval = nand_write(nand, offset, length, buffer);
if (rval == 0)
@ -626,36 +656,58 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
*
* Read image from NAND flash.
* Blocks that are marked bad are skipped and the next block is read
* instead as long as the image is short enough to fit even after skipping the
* bad blocks.
* instead as long as the image is short enough to fit even after
* skipping the bad blocks. Due to bad blocks we may not be able to
* perform the requested read. In the case where the read would extend
* beyond the end of the NAND device, both length and actual (if not
* NULL) are set to 0. In the case where the read would extend beyond
* the limit we are passed, length is set to 0 and actual is set to the
* required length.
*
* @param nand NAND device
* @param offset offset in flash
* @param length buffer length, on return holds number of read bytes
* @param actual set to size required to read length worth of buffer or 0
* on error, if not NULL
* @param lim maximum size that actual may be in order to not exceed the
* buffer
* @param buffer buffer to write to
* @return 0 in case of success
*/
int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
u_char *buffer)
size_t *actual, loff_t lim, u_char *buffer)
{
int rval;
size_t left_to_read = *length;
size_t used_for_read = 0;
u_char *p_buffer = buffer;
int need_skip;
if ((offset & (nand->writesize - 1)) != 0) {
printf("Attempt to read non page-aligned data\n");
*length = 0;
if (actual)
*actual = 0;
return -EINVAL;
}
need_skip = check_skip_len(nand, offset, *length);
need_skip = check_skip_len(nand, offset, *length, &used_for_read);
if (actual)
*actual = used_for_read;
if (need_skip < 0) {
printf("Attempt to read outside the flash area\n");
*length = 0;
return -EINVAL;
}
if (used_for_read > lim) {
puts("Size of read exceeds partition or device limit\n");
*length = 0;
return -EFBIG;
}
if (!need_skip) {
rval = nand_read(nand, offset, length, buffer);
if (!rval || rval == -EUCLEAN)

View File

@ -39,6 +39,8 @@
#define CONFIG_SETUP_MEMORY_TAGS
#define CONFIG_INITRD_TAG
#define CONFIG_SYS_CACHELINE_SIZE 64
/* commands to include */
#include <config_cmd_default.h>
@ -60,6 +62,11 @@
"fdtfile=\0" \
"console=ttyO0,115200n8\0" \
"optargs=\0" \
"mtdids=" MTDIDS_DEFAULT "\0" \
"mtdparts=" MTDPARTS_DEFAULT "\0" \
"dfu_alt_info_mmc=" DFU_ALT_INFO_MMC "\0" \
"dfu_alt_info_emmc=rawemmc mmc 0 3751936\0" \
"dfu_alt_info_nand=" DFU_ALT_INFO_NAND "\0" \
"mmcdev=0\0" \
"mmcroot=/dev/mmcblk0p2 ro\0" \
"mmcrootfstype=ext4 rootwait\0" \
@ -167,8 +174,8 @@
#define CONFIG_CMD_ECHO
/* max number of command args */
#define CONFIG_SYS_MAXARGS 16
/* We set the max number of command args high to avoid HUSH bugs. */
#define CONFIG_SYS_MAXARGS 64
/* Console I/O Buffer Size */
#define CONFIG_SYS_CBSIZE 512
@ -197,6 +204,7 @@
#define CONFIG_CMD_MMC
#define CONFIG_DOS_PARTITION
#define CONFIG_CMD_FAT
#define CONFIG_FAT_WRITE
#define CONFIG_CMD_EXT2
#define CONFIG_CMD_EXT4
#define CONFIG_CMD_FS_GENERIC
@ -209,6 +217,38 @@
#define CONFIG_CMD_SF
#define CONFIG_SF_DEFAULT_SPEED (24000000)
/* USB Composite download gadget - g_dnl */
#define CONFIG_USB_GADGET
#define CONFIG_USBDOWNLOAD_GADGET
/* USB TI's IDs */
#define CONFIG_USBD_HS
#define CONFIG_G_DNL_VENDOR_NUM 0x0403
#define CONFIG_G_DNL_PRODUCT_NUM 0xBD00
#define CONFIG_G_DNL_MANUFACTURER "Texas Instruments"
/* USB Device Firmware Update support */
#define CONFIG_DFU_FUNCTION
#define CONFIG_DFU_MMC
#define CONFIG_DFU_NAND
#define CONFIG_CMD_DFU
#define DFU_ALT_INFO_MMC \
"boot part 0 1;" \
"rootfs part 0 2;" \
"MLO fat 0 1;" \
"MLO.raw mmc 100 100;" \
"u-boot.img.raw mmc 300 3C0;" \
"u-boot.img fat 0 1;" \
"uEnv.txt fat 0 1"
#define DFU_ALT_INFO_NAND \
"SPL part 0 1;" \
"SPL.backup1 part 0 2;" \
"SPL.backup2 part 0 3;" \
"SPL.backup3 part 0 4;" \
"u-boot part 0 5;" \
"kernel part 0 7;" \
"rootfs part 0 8"
/* Physical Memory Map */
#define CONFIG_NR_DRAM_BANKS 1 /* 1 bank of DRAM */
#define PHYS_DRAM_1 0x80000000 /* DRAM Bank #1 */
@ -353,6 +393,7 @@
#define CONFIG_MUSB_GADGET
#define CONFIG_MUSB_PIO_ONLY
#define CONFIG_USB_GADGET_DUALSPEED
#define CONFIG_USB_GADGET_VBUS_DRAW 2
#define CONFIG_MUSB_HOST
#define CONFIG_AM335X_USB0
#define CONFIG_AM335X_USB0_MODE MUSB_PERIPHERAL
@ -424,6 +465,13 @@
/* NAND support */
#ifdef CONFIG_NAND
#define CONFIG_CMD_NAND
#define CONFIG_CMD_MTDPARTS
#define MTDIDS_DEFAULT "nand0=omap2-nand.0"
#define MTDPARTS_DEFAULT "mtdparts=omap2-nand.0:128k(SPL)," \
"128k(SPL.backup1)," \
"128k(SPL.backup2)," \
"128k(SPL.backup3),1920k(u-boot)," \
"128k(u-boot-env),5m(kernel),-(rootfs)"
#define CONFIG_NAND_OMAP_GPMC
#define GPMC_NAND_ECC_LP_x16_LAYOUT 1
#define CONFIG_SYS_NAND_BASE (0x08000000) /* physical address */

View File

@ -52,14 +52,26 @@ 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;
}
#define DFU_NAME_SIZE 32
#define DFU_CMD_BUF_SIZE 128
#define DFU_DATA_BUF_SIZE (1024*1024*8) /* 8 MiB */
#define DFU_NAME_SIZE 32
#define DFU_CMD_BUF_SIZE 128
#define DFU_DATA_BUF_SIZE (1024*1024*8) /* 8 MiB */
#ifndef CONFIG_SYS_DFU_MAX_FILE_SIZE
#define CONFIG_SYS_DFU_MAX_FILE_SIZE (4 << 20) /* 4 MiB */
#endif
struct dfu_entity {
char name[DFU_NAME_SIZE];
@ -71,12 +83,32 @@ struct dfu_entity {
union {
struct mmc_internal_data mmc;
struct nand_internal_data nand;
} data;
int (*read_medium)(struct dfu_entity *dfu, void *buf, long *len);
int (*write_medium)(struct dfu_entity *dfu, void *buf, long *len);
int (*read_medium)(struct dfu_entity *dfu,
u64 offset, void *buf, long *len);
int (*write_medium)(struct dfu_entity *dfu,
u64 offset, void *buf, long *len);
int (*flush_medium)(struct dfu_entity *dfu);
struct list_head list;
/* on the fly state */
u32 crc;
u64 offset;
int i_blk_seq_num;
u8 *i_buf;
u8 *i_buf_start;
u8 *i_buf_end;
long r_left;
long b_left;
u32 bad_skip; /* for nand use */
unsigned int inited:1;
};
int dfu_config_entities(char *s, char *interface, int num);
@ -100,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_ */

View File

@ -129,7 +129,7 @@ struct nand_erase_options {
typedef struct nand_erase_options nand_erase_options_t;
int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
u_char *buffer);
size_t *actual, loff_t lim, u_char *buffer);
#define WITH_YAFFS_OOB (1 << 0) /* whether write with yaffs format. This flag
* is a 'mode' meaning it cannot be mixed with
@ -137,7 +137,7 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
#define WITH_DROP_FFS (1 << 1) /* drop trailing all-0xff pages */
int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
u_char *buffer, int flags);
size_t *actual, loff_t lim, u_char *buffer, int flags);
int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts);
int nand_torture(nand_info_t *nand, loff_t offset);