summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/cmd_setexpr.c19
-rw-r--r--common/cmd_sf.c209
-rw-r--r--common/usb.c12
-rw-r--r--common/usb_hub.c35
-rw-r--r--common/usb_storage.c10
5 files changed, 275 insertions, 10 deletions
diff --git a/common/cmd_setexpr.c b/common/cmd_setexpr.c
index 7b140deea39..5a042951da5 100644
--- a/common/cmd_setexpr.c
+++ b/common/cmd_setexpr.c
@@ -57,12 +57,22 @@ static int do_setexpr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
int w;
/* Validate arguments */
- if ((argc != 5) || (strlen(argv[3]) != 1))
+ if (argc != 5 && argc != 3)
+ return CMD_RET_USAGE;
+ if (argc == 5 && strlen(argv[3]) != 1)
return CMD_RET_USAGE;
w = cmd_get_data_size(argv[0], 4);
a = get_arg(argv[2], w);
+
+ if (argc == 3) {
+ sprintf(buf, "%lx", a);
+ setenv(argv[1], buf);
+
+ return 0;
+ }
+
b = get_arg(argv[4], w);
switch (argv[3][0]) {
@@ -87,8 +97,11 @@ static int do_setexpr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
U_BOOT_CMD(
setexpr, 5, 0, do_setexpr,
"set environment variable as the result of eval expression",
- "[.b, .w, .l] name value1 <op> value2\n"
+ "[.b, .w, .l] name [*]value1 <op> [*]value2\n"
" - set environment variable 'name' to the result of the evaluated\n"
" express specified by <op>. <op> can be &, |, ^, +, -, *, /, %\n"
- " size argument is only meaningful if value1 and/or value2 are memory addresses"
+ " size argument is only meaningful if value1 and/or value2 are\n"
+ " memory addresses (*)\n"
+ "setexpr[.b, .w, .l] name *value\n"
+ " - load a memory address into a variable"
);
diff --git a/common/cmd_sf.c b/common/cmd_sf.c
index 5ac1d0c4c1e..b1753587d3c 100644
--- a/common/cmd_sf.c
+++ b/common/cmd_sf.c
@@ -5,6 +5,7 @@
* Licensed under the GPL-2 or later.
*/
+#include <div64.h>
#include <common.h>
#include <malloc.h>
#include <spi_flash.h>
@@ -67,6 +68,23 @@ static int sf_parse_len_arg(char *arg, ulong *len)
return 1;
}
+/**
+ * This function takes a byte length and a delta unit of time to compute the
+ * approximate bytes per second
+ *
+ * @param len amount of bytes currently processed
+ * @param start_ms start time of processing in ms
+ * @return bytes per second if OK, 0 on error
+ */
+static ulong bytes_per_second(unsigned int len, ulong start_ms)
+{
+ /* less accurate but avoids overflow */
+ if (len >= ((unsigned int) -1) / 1024)
+ return len / (max(get_timer(start_ms) / 1024, 1));
+ else
+ return 1024 * len / max(get_timer(start_ms), 1);
+}
+
static int do_spi_flash_probe(int argc, char * const argv[])
{
unsigned int bus = CONFIG_SF_DEFAULT_BUS;
@@ -167,11 +185,26 @@ static int spi_flash_update(struct spi_flash *flash, u32 offset,
const char *end = buf + len;
size_t todo; /* number of bytes to do in this pass */
size_t skipped = 0; /* statistics */
+ const ulong start_time = get_timer(0);
+ size_t scale = 1;
+ const char *start_buf = buf;
+ ulong delta;
+ if (end - buf >= 200)
+ scale = (end - buf) / 100;
cmp_buf = malloc(flash->sector_size);
if (cmp_buf) {
+ ulong last_update = get_timer(0);
+
for (; buf < end && !err_oper; buf += todo, offset += todo) {
todo = min(end - buf, flash->sector_size);
+ if (get_timer(last_update) > 100) {
+ printf(" \rUpdating, %zu%% %lu B/s",
+ 100 - (end - buf) / scale,
+ bytes_per_second(buf - start_buf,
+ start_time));
+ last_update = get_timer(0);
+ }
err_oper = spi_flash_update_block(flash, offset, todo,
buf, cmp_buf, &skipped);
}
@@ -179,12 +212,17 @@ static int spi_flash_update(struct spi_flash *flash, u32 offset,
err_oper = "malloc";
}
free(cmp_buf);
+ putc('\r');
if (err_oper) {
printf("SPI flash failed in %s step\n", err_oper);
return 1;
}
- printf("%zu bytes written, %zu bytes skipped\n", len - skipped,
- skipped);
+
+ delta = get_timer(start_time);
+ printf("%zu bytes written, %zu bytes skipped", len - skipped,
+ skipped);
+ printf(" in %ld.%lds, speed %ld B/s\n",
+ delta / 1000, delta % 1000, bytes_per_second(len, start_time));
return 0;
}
@@ -275,6 +313,161 @@ static int do_spi_flash_erase(int argc, char * const argv[])
return 0;
}
+#ifdef CONFIG_CMD_SF_TEST
+enum {
+ STAGE_ERASE,
+ STAGE_CHECK,
+ STAGE_WRITE,
+ STAGE_READ,
+
+ STAGE_COUNT,
+};
+
+static char *stage_name[STAGE_COUNT] = {
+ "erase",
+ "check",
+ "write",
+ "read",
+};
+
+struct test_info {
+ int stage;
+ int bytes;
+ unsigned base_ms;
+ unsigned time_ms[STAGE_COUNT];
+};
+
+static void show_time(struct test_info *test, int stage)
+{
+ uint64_t speed; /* KiB/s */
+ int bps; /* Bits per second */
+
+ speed = (long long)test->bytes * 1000;
+ do_div(speed, test->time_ms[stage] * 1024);
+ bps = speed * 8;
+
+ printf("%d %s: %d ticks, %d KiB/s %d.%03d Mbps\n", stage,
+ stage_name[stage], test->time_ms[stage],
+ (int)speed, bps / 1000, bps % 1000);
+}
+
+static void spi_test_next_stage(struct test_info *test)
+{
+ test->time_ms[test->stage] = get_timer(test->base_ms);
+ show_time(test, test->stage);
+ test->base_ms = get_timer(0);
+ test->stage++;
+}
+
+/**
+ * Run a test on the SPI flash
+ *
+ * @param flash SPI flash to use
+ * @param buf Source buffer for data to write
+ * @param len Size of data to read/write
+ * @param offset Offset within flash to check
+ * @param vbuf Verification buffer
+ * @return 0 if ok, -1 on error
+ */
+static int spi_flash_test(struct spi_flash *flash, char *buf, ulong len,
+ ulong offset, char *vbuf)
+{
+ struct test_info test;
+ int i;
+
+ printf("SPI flash test:\n");
+ memset(&test, '\0', sizeof(test));
+ test.base_ms = get_timer(0);
+ test.bytes = len;
+ if (spi_flash_erase(flash, offset, len)) {
+ printf("Erase failed\n");
+ return -1;
+ }
+ spi_test_next_stage(&test);
+
+ if (spi_flash_read(flash, offset, len, vbuf)) {
+ printf("Check read failed\n");
+ return -1;
+ }
+ for (i = 0; i < len; i++) {
+ if (vbuf[i] != 0xff) {
+ printf("Check failed at %d\n", i);
+ print_buffer(i, vbuf + i, 1, min(len - i, 0x40), 0);
+ return -1;
+ }
+ }
+ spi_test_next_stage(&test);
+
+ if (spi_flash_write(flash, offset, len, buf)) {
+ printf("Write failed\n");
+ return -1;
+ }
+ memset(vbuf, '\0', len);
+ spi_test_next_stage(&test);
+
+ if (spi_flash_read(flash, offset, len, vbuf)) {
+ printf("Read failed\n");
+ return -1;
+ }
+ spi_test_next_stage(&test);
+
+ for (i = 0; i < len; i++) {
+ if (buf[i] != vbuf[i]) {
+ printf("Verify failed at %d, good data:\n", i);
+ print_buffer(i, buf + i, 1, min(len - i, 0x40), 0);
+ printf("Bad data:\n");
+ print_buffer(i, vbuf + i, 1, min(len - i, 0x40), 0);
+ return -1;
+ }
+ }
+ printf("Test passed\n");
+ for (i = 0; i < STAGE_COUNT; i++)
+ show_time(&test, i);
+
+ return 0;
+}
+
+static int do_spi_flash_test(int argc, char * const argv[])
+{
+ unsigned long offset;
+ unsigned long len;
+ char *buf = (char *)CONFIG_SYS_TEXT_BASE;
+ char *endp;
+ char *vbuf;
+ int ret;
+
+ offset = simple_strtoul(argv[1], &endp, 16);
+ if (*argv[1] == 0 || *endp != 0)
+ return -1;
+ len = simple_strtoul(argv[2], &endp, 16);
+ if (*argv[2] == 0 || *endp != 0)
+ return -1;
+
+ vbuf = malloc(len);
+ if (!vbuf) {
+ printf("Cannot allocate memory\n");
+ return 1;
+ }
+ buf = malloc(len);
+ if (!buf) {
+ free(vbuf);
+ printf("Cannot allocate memory\n");
+ return 1;
+ }
+
+ memcpy(buf, (char *)CONFIG_SYS_TEXT_BASE, len);
+ ret = spi_flash_test(flash, buf, len, offset, vbuf);
+ free(vbuf);
+ free(buf);
+ if (ret) {
+ printf("Test failed\n");
+ return 1;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_CMD_SF_TEST */
+
static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
const char *cmd;
@@ -304,6 +497,10 @@ static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[
ret = do_spi_flash_read_write(argc, argv);
else if (strcmp(cmd, "erase") == 0)
ret = do_spi_flash_erase(argc, argv);
+#ifdef CONFIG_CMD_SF_TEST
+ else if (!strcmp(cmd, "test"))
+ ret = do_spi_flash_test(argc, argv);
+#endif
else
ret = -1;
@@ -315,6 +512,13 @@ usage:
return CMD_RET_USAGE;
}
+#ifdef CONFIG_CMD_SF_TEST
+#define SF_TEST_HELP "\nsf test offset len " \
+ "- run a very basic destructive test"
+#else
+#define SF_TEST_HELP
+#endif
+
U_BOOT_CMD(
sf, 5, 1, do_spi_flash,
"SPI flash sub-system",
@@ -328,4 +532,5 @@ U_BOOT_CMD(
" `+len' round up `len' to block size\n"
"sf update addr offset len - erase and write `len' bytes from memory\n"
" at `addr' to flash at `offset'"
+ SF_TEST_HELP
);
diff --git a/common/usb.c b/common/usb.c
index ac9b4ca8d58..6fc0fc1c0ec 100644
--- a/common/usb.c
+++ b/common/usb.c
@@ -805,6 +805,18 @@ struct usb_device *usb_alloc_new_device(void *controller)
return &usb_dev[dev_index - 1];
}
+/*
+ * Free the newly created device node.
+ * Called in error cases where configuring a newly attached
+ * device fails for some reason.
+ */
+void usb_free_device(void)
+{
+ dev_index--;
+ USB_PRINTF("Freeing device node: %d\n", dev_index);
+ memset(&usb_dev[dev_index], 0, sizeof(struct usb_device));
+ usb_dev[dev_index].devnum = -1;
+}
/*
* By the time we get here, the device has gotten a new device ID
diff --git a/common/usb_hub.c b/common/usb_hub.c
index e4a120120dc..b5eeb62fbe5 100644
--- a/common/usb_hub.c
+++ b/common/usb_hub.c
@@ -259,6 +259,8 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port)
/* Run it through the hoops (find a driver, etc) */
if (usb_new_device(usb)) {
/* Woops, disable the port */
+ usb_free_device();
+ dev->children[port] = NULL;
USB_HUB_PRINTF("hub: disabling port %d\n", port + 1);
usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE);
}
@@ -396,14 +398,37 @@ static int usb_hub_configure(struct usb_device *dev)
for (i = 0; i < dev->maxchild; i++) {
ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1);
unsigned short portstatus, portchange;
+ int ret;
+ ulong start = get_timer(0);
+
+ /*
+ * Wait for (whichever finishes first)
+ * - A maximum of 10 seconds
+ * This is a purely observational value driven by connecting
+ * a few broken pen drives and taking the max * 1.5 approach
+ * - connection_change and connection state to report same
+ * state
+ */
+ do {
+ ret = usb_get_port_status(dev, i + 1, portsts);
+ if (ret < 0) {
+ USB_HUB_PRINTF("get_port_status failed\n");
+ break;
+ }
+
+ portstatus = le16_to_cpu(portsts->wPortStatus);
+ portchange = le16_to_cpu(portsts->wPortChange);
+
+ if ((portchange & USB_PORT_STAT_C_CONNECTION) ==
+ (portstatus & USB_PORT_STAT_CONNECTION))
+ break;
+
+ mdelay(100);
+ } while (get_timer(start) < CONFIG_SYS_HZ * 10);
- if (usb_get_port_status(dev, i + 1, portsts) < 0) {
- USB_HUB_PRINTF("get_port_status failed\n");
+ if (ret < 0)
continue;
- }
- portstatus = le16_to_cpu(portsts->wPortStatus);
- portchange = le16_to_cpu(portsts->wPortChange);
USB_HUB_PRINTF("Port %d Status %X Change %X\n",
i + 1, portstatus, portchange);
diff --git a/common/usb_storage.c b/common/usb_storage.c
index 2d92ee1bb3e..fb322b4015f 100644
--- a/common/usb_storage.c
+++ b/common/usb_storage.c
@@ -970,6 +970,16 @@ static int usb_test_unit_ready(ccb *srb, struct us_data *ss)
return 0;
}
usb_request_sense(srb, ss);
+ /*
+ * Check the Key Code Qualifier, if it matches
+ * "Not Ready - medium not present"
+ * (the sense Key equals 0x2 and the ASC is 0x3a)
+ * return immediately as the medium being absent won't change
+ * unless there is a user action.
+ */
+ if ((srb->sense_buf[2] == 0x02) &&
+ (srb->sense_buf[12] == 0x3a))
+ return -1;
mdelay(100);
} while (retries--);