summaryrefslogtreecommitdiffstats
path: root/scripts/imx/imx-usb-loader.c
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/imx/imx-usb-loader.c')
-rw-r--r--scripts/imx/imx-usb-loader.c281
1 files changed, 265 insertions, 16 deletions
diff --git a/scripts/imx/imx-usb-loader.c b/scripts/imx/imx-usb-loader.c
index 9de7bb3a8a..90ee3e09d5 100644
--- a/scripts/imx/imx-usb-loader.c
+++ b/scripts/imx/imx-usb-loader.c
@@ -45,6 +45,12 @@
#define FT_DCD 0xee
#define FT_LOAD_ONLY 0x00
+/*
+ * comment from libusb:
+ * As per the USB 3.0 specs, the current maximum limit for the depth is 7.
+ */
+#define MAX_USB_PORTS 7
+
int verbose;
static struct libusb_device_handle *usb_dev_handle;
static struct usb_id *usb_id;
@@ -63,6 +69,9 @@ struct mach_id {
#define HDR_MX53 2
unsigned char header_type;
unsigned short max_transfer;
+#define DEV_IMX 0
+#define DEV_MXS 1
+ unsigned char dev_type;
};
struct usb_work {
@@ -81,7 +90,9 @@ static const struct mach_id imx_ids[] = {
.vid = 0x066f,
.pid = 0x3780,
.name = "i.MX23",
- .mode = MODE_BULK,
+ .mode = MODE_HID,
+ .max_transfer = 1024,
+ .dev_type = DEV_MXS,
}, {
.vid = 0x15a2,
.pid = 0x0030,
@@ -114,6 +125,8 @@ static const struct mach_id imx_ids[] = {
.vid = 0x15a2,
.pid = 0x004f,
.name = "i.MX28",
+ .max_transfer = 1024,
+ .dev_type = DEV_MXS,
}, {
.vid = 0x15a2,
.pid = 0x0052,
@@ -181,6 +194,17 @@ struct sdp_command {
uint8_t rsvd;
} __attribute__((packed));
+#define MXS_CMD_FW_DOWNLOAD 0x02
+struct mxs_command {
+ uint32_t sign; /* Signature */
+ uint32_t tag; /* Tag */
+ uint32_t size; /* Payload size */
+ uint8_t flags; /* Flags (host to device) */
+ uint8_t rsvd[2]; /* Reserved */
+ uint8_t cmd; /* Firmware download */
+ uint32_t dw_size; /* Download size */
+} __attribute__((packed));
+
static const struct mach_id *imx_device(unsigned short vid, unsigned short pid)
{
int i;
@@ -197,9 +221,66 @@ static const struct mach_id *imx_device(unsigned short vid, unsigned short pid)
return NULL;
}
-static libusb_device *find_imx_dev(libusb_device **devs, const struct mach_id **pp_id)
+static int device_location_equal(libusb_device *device, const char *location)
+{
+ uint8_t port_path[MAX_USB_PORTS];
+ uint8_t dev_bus;
+ int path_step, path_len;
+ int result = 0;
+ char *ptr, *loc;
+
+ /* strtok need non const char */
+ loc = strdup(location);
+
+ path_len = libusb_get_port_numbers(device, port_path, MAX_USB_PORTS);
+ if (path_len == LIBUSB_ERROR_OVERFLOW) {
+ fprintf(stderr, "cannot determine path to usb device! (more than %i ports in path)\n",
+ MAX_USB_PORTS);
+ goto done;
+ }
+
+ ptr = strtok(loc, "-");
+ if (ptr == NULL) {
+ printf("no '-' in path\n");
+ goto done;
+ }
+
+ dev_bus = libusb_get_bus_number(device);
+ /* check bus mismatch */
+ if (atoi(ptr) != dev_bus)
+ goto done;
+
+ path_step = 0;
+ while (path_step < MAX_USB_PORTS) {
+ ptr = strtok(NULL, ".");
+
+ /* no more tokens in path */
+ if (ptr == NULL)
+ break;
+
+ /* path mismatch at some step */
+ if (path_step < path_len && atoi(ptr) != port_path[path_step])
+ break;
+
+ path_step++;
+ };
+
+ /* walked the full path, all elements match */
+ if (path_step == path_len)
+ result = 1;
+ else
+ fprintf(stderr, " excluded by device path option\n");
+
+done:
+ free(loc);
+ return result;
+}
+
+static libusb_device *find_imx_dev(libusb_device **devs, const struct mach_id **pp_id,
+ const char *location)
{
int i = 0;
+ int err;
const struct mach_id *p;
for (;;) {
@@ -217,10 +298,24 @@ static libusb_device *find_imx_dev(libusb_device **devs, const struct mach_id **
}
p = imx_device(desc.idVendor, desc.idProduct);
- if (p) {
- *pp_id = p;
- return dev;
+ if (!p)
+ continue;
+
+ err = libusb_open(dev, &usb_dev_handle);
+ if (err) {
+ fprintf(stderr, "Could not open device vid=0x%x pid=0x%x err=%d\n",
+ p->vid, p->pid, err);
+ continue;
+ }
+
+ if (location && !device_location_equal(dev, location)) {
+ libusb_close(usb_dev_handle);
+ usb_dev_handle = NULL;
+ continue;
}
+
+ *pp_id = p;
+ return dev;
}
*pp_id = NULL;
@@ -798,6 +893,98 @@ static int do_dcd_v2_cmd_write(const unsigned char *dcd)
return 0;
}
+static int do_dcd_v2_cmd_check(const unsigned char *dcd)
+{
+ uint32_t mask;
+ uint32_t poll_count = 0;
+ int bytes;
+ enum imx_dcd_v2_check_cond cond;
+ struct imx_dcd_v2_check *check = (struct imx_dcd_v2_check *) dcd;
+ switch (ntohs(check->length)) {
+ case 12:
+ /* poll indefinitely */
+ poll_count = 0xffffffff;
+ break;
+ case 16:
+ poll_count = ntohl(check->count);
+ if (poll_count == 0)
+ /* this command behaves as for NOP */
+ return 0;
+ break;
+ default:
+ fprintf(stderr, "Error: invalid DCD check length\n");
+ return -1;
+ }
+
+ switch (check->param & 7) {
+ case 1:
+ case 2:
+ case 4:
+ bytes = check->param & 7;
+ break;
+ default:
+ fprintf(stderr, "Error: invalid DCD check size\n");
+ return -1;
+ }
+
+ switch ((check->param & 0xf8) >> 3) {
+ case check_all_bits_clear:
+ case check_all_bits_set:
+ case check_any_bit_clear:
+ case check_any_bit_set:
+ cond = (check->param & 0xf8) >> 3;
+ break;
+ default:
+ fprintf(stderr, "Error: invalid DCD check condition\n");
+ return -1;
+ }
+
+ mask = ntohl(check->mask);
+
+ fprintf(stderr, "DCD check condition %i on address 0x%x\n",
+ cond, ntohl(check->addr));
+ /* Reduce the poll count to some arbitrary practical limit.
+ Polling via SRP commands will be much slower compared to
+ polling when DCD is interpreted by the SOC microcode.
+ */
+ if (poll_count > 1000)
+ poll_count = 1000;
+
+ while (poll_count > 0) {
+ uint32_t data = 0;
+ int ret = read_memory(ntohl(check->addr), &data, bytes);
+ if (ret < 0)
+ return ret;
+
+ data &= mask;
+
+ switch (cond) {
+ case check_all_bits_clear:
+ if (data != 0)
+ return 0;
+ break;
+ case check_all_bits_set:
+ if (data != mask)
+ return 0;
+ break;
+ case check_any_bit_clear:
+ if (data == mask)
+ return 0;
+ break;
+ case check_any_bit_set:
+ if (data == 0)
+ return 0;
+ break;
+ }
+ poll_count--;
+ }
+
+ fprintf(stderr, "Error: timeout waiting for DCD check condition %i "
+ "on address 0x%08x to match 0x%08x\n", cond,
+ ntohl(check->addr), ntohl(check->mask));
+ return -1;
+}
+
static int process_dcd_table_ivt(const struct imx_flash_header_v2 *hdr,
const unsigned char *file_start, unsigned cnt)
{
@@ -850,8 +1037,7 @@ static int process_dcd_table_ivt(const struct imx_flash_header_v2 *hdr,
ret = do_dcd_v2_cmd_write(dcd);
break;
case TAG_CHECK:
- fprintf(stderr, "DCD check not implemented yet\n");
- usleep(50000);
+ ret = do_dcd_v2_cmd_check(dcd);
break;
case TAG_UNLOCK:
fprintf(stderr, "DCD unlock not implemented yet\n");
@@ -1273,6 +1459,66 @@ static int write_mem(const struct config_data *data, uint32_t addr,
return modify_memory(addr, val, width, set_bits, clear_bits);
}
+/* MXS section */
+static int mxs_load_file(libusb_device_handle *dev, uint8_t *data, int size)
+{
+ static struct mxs_command dl_command;
+ int last_trans, err;
+ void *p;
+ int cnt;
+
+ dl_command.sign = htonl(0x424c5443); /* Signature: BLTC */
+ dl_command.tag = htonl(0x1);
+ dl_command.size = htonl(size);
+ dl_command.flags = 0;
+ dl_command.rsvd[0] = 0;
+ dl_command.rsvd[1] = 0;
+ dl_command.cmd = MXS_CMD_FW_DOWNLOAD;
+ dl_command.dw_size = htonl(size);
+
+ err = transfer(1, (unsigned char *) &dl_command, 20, &last_trans);
+ if (err) {
+ printf("transfer error at init step: err=%i, last_trans=%i\n",
+ err, last_trans);
+ return err;
+ }
+
+ p = data;
+ cnt = size;
+
+ while (1) {
+ int now = get_min(cnt, usb_id->mach_id->max_transfer);
+
+ if (!now)
+ break;
+
+ err = transfer(2, p, now, &now);
+ if (err) {
+ printf("dl_command err=%i, last_trans=%i\n", err, now);
+ return err;
+ }
+
+ p += now;
+ cnt -= now;
+ }
+
+ return err;
+}
+
+static int mxs_work(struct usb_work *curr)
+{
+ unsigned fsize = 0;
+ unsigned char *buf = NULL;
+ int ret;
+
+ ret = read_file(curr->filename, &buf, &fsize);
+ if (ret < 0)
+ return ret;
+
+ return mxs_load_file(usb_dev_handle, buf, fsize);
+}
+/* end of mxs section */
+
static int parse_initfile(const char *filename)
{
struct config_data data = {
@@ -1287,6 +1533,7 @@ static void usage(const char *prgname)
fprintf(stderr, "usage: %s [OPTIONS] [FILENAME]\n\n"
"-c check correctness of flashed image\n"
"-i <cfgfile> Specify custom SoC initialization file\n"
+ "-p <devpath> Specify device path: <bus>-<port>[.<port>]...\n"
"-s skip DCD included in image\n"
"-v verbose (give multiple times to increase)\n"
"-h this help\n", prgname);
@@ -1307,10 +1554,11 @@ int main(int argc, char *argv[])
struct usb_work w = {};
int opt;
char *initfile = NULL;
+ char *devpath = NULL;
w.do_dcd_once = 1;
- while ((opt = getopt(argc, argv, "cvhi:s")) != -1) {
+ while ((opt = getopt(argc, argv, "cvhi:p:s")) != -1) {
switch (opt) {
case 'c':
verify = 1;
@@ -1323,6 +1571,9 @@ int main(int argc, char *argv[])
case 'i':
initfile = optarg;
break;
+ case 'p':
+ devpath = optarg;
+ break;
case 's':
w.do_dcd_once = 0;
break;
@@ -1350,19 +1601,12 @@ int main(int argc, char *argv[])
goto out;
}
- dev = find_imx_dev(devs, &mach);
+ dev = find_imx_dev(devs, &mach, devpath);
if (!dev) {
fprintf(stderr, "no supported device found\n");
goto out;
}
- err = libusb_open(dev, &usb_dev_handle);
- if (err) {
- fprintf(stderr, "Could not open device vid=0x%x pid=0x%x err=%d\n",
- mach->vid, mach->pid, err);
- goto out;
- }
-
libusb_free_device_list(devs, 1);
libusb_get_configuration(usb_dev_handle, &config);
@@ -1384,6 +1628,11 @@ int main(int argc, char *argv[])
usb_id->mach_id = mach;
+ if (mach->dev_type == DEV_MXS) {
+ ret = mxs_work(&w);
+ goto out;
+ }
+
err = do_status();
if (err) {
printf("status failed\n");