summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/dfu/dfu.c4
-rw-r--r--drivers/dfu/dfu_mmc.c46
-rw-r--r--drivers/dfu/dfu_nand.c13
-rw-r--r--drivers/usb/gadget/Makefile1
-rw-r--r--drivers/usb/gadget/ci_udc.c182
-rw-r--r--drivers/usb/gadget/ci_udc.h17
-rw-r--r--drivers/usb/gadget/f_dfu.c10
-rw-r--r--drivers/usb/gadget/f_fastboot.c513
-rw-r--r--drivers/usb/gadget/f_thor.c12
-rw-r--r--drivers/usb/gadget/storage_common.c4
-rw-r--r--drivers/usb/host/ehci-tegra.c165
-rw-r--r--drivers/usb/musb-new/musb_gadget_ep0.c8
12 files changed, 832 insertions, 143 deletions
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c
index 51b10263b8..a93810934a 100644
--- a/drivers/dfu/dfu.c
+++ b/drivers/dfu/dfu.c
@@ -131,6 +131,10 @@ int dfu_flush(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
{
int ret = 0;
+ ret = dfu_write_buffer_drain(dfu);
+ if (ret)
+ return ret;
+
if (dfu->flush_medium)
ret = dfu->flush_medium(dfu);
diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c
index 5e10ea7e66..63cc876612 100644
--- a/drivers/dfu/dfu_mmc.c
+++ b/drivers/dfu/dfu_mmc.c
@@ -18,11 +18,29 @@ 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_access_part(struct dfu_entity *dfu, struct mmc *mmc, int part)
+{
+ int ret;
+
+ if (part == mmc->part_num)
+ return 0;
+
+ ret = mmc_switch_part(dfu->dev_num, part);
+ if (ret) {
+ error("Cannot switch to partition %d\n", part);
+ return ret;
+ }
+ mmc->part_num = part;
+
+ return 0;
+}
+
static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
u64 offset, void *buf, long *len)
{
struct mmc *mmc = find_mmc_device(dfu->dev_num);
u32 blk_start, blk_count, n = 0;
+ int ret, part_num_bkp = 0;
/*
* We must ensure that we work in lba_blk_size chunks, so ALIGN
@@ -39,6 +57,13 @@ static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
return -EINVAL;
}
+ if (dfu->data.mmc.hw_partition >= 0) {
+ part_num_bkp = mmc->part_num;
+ ret = mmc_access_part(dfu, mmc, dfu->data.mmc.hw_partition);
+ if (ret)
+ return ret;
+ }
+
debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__,
op == DFU_OP_READ ? "MMC READ" : "MMC WRITE", dfu->dev_num,
blk_start, blk_count, buf);
@@ -57,9 +82,17 @@ static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
if (n != blk_count) {
error("MMC operation failed");
+ if (dfu->data.mmc.hw_partition >= 0)
+ mmc_access_part(dfu, mmc, part_num_bkp);
return -EIO;
}
+ if (dfu->data.mmc.hw_partition >= 0) {
+ ret = mmc_access_part(dfu, mmc, part_num_bkp);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
@@ -194,6 +227,8 @@ int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf,
* 2nd and 3rd:
* lba_start and lba_size, for raw write
* mmc_dev and mmc_part, for filesystems and part
+ * 4th (optional):
+ * mmcpart <num> (access to HW eMMC partitions)
*/
int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)
{
@@ -233,11 +268,22 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)
return -ENODEV;
}
+ dfu->data.mmc.hw_partition = -EINVAL;
if (!strcmp(entity_type, "raw")) {
dfu->layout = DFU_RAW_ADDR;
dfu->data.mmc.lba_start = second_arg;
dfu->data.mmc.lba_size = third_arg;
dfu->data.mmc.lba_blk_size = mmc->read_bl_len;
+
+ /*
+ * Check for an extra entry at dfu_alt_info env variable
+ * specifying the mmc HW defined partition number
+ */
+ if (s)
+ if (!strcmp(strsep(&s, " "), "mmcpart"))
+ dfu->data.mmc.hw_partition =
+ simple_strtoul(s, NULL, 0);
+
} else if (!strcmp(entity_type, "part")) {
disk_partition_t partinfo;
block_dev_desc_t *blk_dev = &mmc->block_dev;
diff --git a/drivers/dfu/dfu_nand.c b/drivers/dfu/dfu_nand.c
index 2d07097e85..ccdbef6b75 100644
--- a/drivers/dfu/dfu_nand.c
+++ b/drivers/dfu/dfu_nand.c
@@ -163,6 +163,18 @@ static int dfu_flush_medium_nand(struct dfu_entity *dfu)
return ret;
}
+unsigned int dfu_polltimeout_nand(struct dfu_entity *dfu)
+{
+ /*
+ * Currently, Poll Timeout != 0 is only needed on nand
+ * ubi partition, as the not used sectors need an erase
+ */
+ if (dfu->data.nand.ubi)
+ return DFU_MANIFEST_POLL_TIMEOUT;
+
+ return DFU_DEFAULT_POLL_TIMEOUT;
+}
+
int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s)
{
char *st;
@@ -211,6 +223,7 @@ int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s)
dfu->read_medium = dfu_read_medium_nand;
dfu->write_medium = dfu_write_medium_nand;
dfu->flush_medium = dfu_flush_medium_nand;
+ dfu->poll_timeout = dfu_polltimeout_nand;
/* initial state */
dfu->inited = 0;
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 896c8d407e..66becdc782 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_THOR_FUNCTION) += f_thor.o
obj-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o
obj-$(CONFIG_DFU_FUNCTION) += f_dfu.o
obj-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o
+obj-$(CONFIG_CMD_FASTBOOT) += f_fastboot.o
endif
ifdef CONFIG_USB_ETHER
obj-y += ether.o
diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c
index 02d3fdade8..9cd003636a 100644
--- a/drivers/usb/gadget/ci_udc.c
+++ b/drivers/usb/gadget/ci_udc.c
@@ -205,13 +205,26 @@ static void ci_invalidate_qtd(int ep_num)
static struct usb_request *
ci_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags)
{
- struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
- return &ci_ep->req;
+ struct ci_req *ci_req;
+
+ ci_req = memalign(ARCH_DMA_MINALIGN, sizeof(*ci_req));
+ if (!ci_req)
+ return NULL;
+
+ INIT_LIST_HEAD(&ci_req->queue);
+ ci_req->b_buf = 0;
+
+ return &ci_req->req;
}
-static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *_req)
+static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *req)
{
- return;
+ struct ci_req *ci_req;
+
+ ci_req = container_of(req, struct ci_req, req);
+ if (ci_req->b_buf)
+ free(ci_req->b_buf);
+ free(ci_req);
}
static void ep_enable(int num, int in, int maxpacket)
@@ -267,99 +280,102 @@ static int ci_ep_disable(struct usb_ep *ep)
return 0;
}
-static int ci_bounce(struct ci_ep *ep, int in)
+static int ci_bounce(struct ci_req *ci_req, int in)
{
- uint32_t addr = (uint32_t)ep->req.buf;
- uint32_t ba;
+ struct usb_request *req = &ci_req->req;
+ uint32_t addr = (uint32_t)req->buf;
+ uint32_t hwaddr;
+ uint32_t aligned_used_len;
/* Input buffer address is not aligned. */
if (addr & (ARCH_DMA_MINALIGN - 1))
goto align;
/* Input buffer length is not aligned. */
- if (ep->req.length & (ARCH_DMA_MINALIGN - 1))
+ if (req->length & (ARCH_DMA_MINALIGN - 1))
goto align;
/* The buffer is well aligned, only flush cache. */
- ep->b_len = ep->req.length;
- ep->b_buf = ep->req.buf;
+ ci_req->hw_len = req->length;
+ ci_req->hw_buf = req->buf;
goto flush;
align:
- /* Use internal buffer for small payloads. */
- if (ep->req.length <= 64) {
- ep->b_len = 64;
- ep->b_buf = ep->b_fast;
- } else {
- ep->b_len = roundup(ep->req.length, ARCH_DMA_MINALIGN);
- ep->b_buf = memalign(ARCH_DMA_MINALIGN, ep->b_len);
- if (!ep->b_buf)
+ if (ci_req->b_buf && req->length > ci_req->b_len) {
+ free(ci_req->b_buf);
+ ci_req->b_buf = 0;
+ }
+ if (!ci_req->b_buf) {
+ ci_req->b_len = roundup(req->length, ARCH_DMA_MINALIGN);
+ ci_req->b_buf = memalign(ARCH_DMA_MINALIGN, ci_req->b_len);
+ if (!ci_req->b_buf)
return -ENOMEM;
}
+ ci_req->hw_len = ci_req->b_len;
+ ci_req->hw_buf = ci_req->b_buf;
+
if (in)
- memcpy(ep->b_buf, ep->req.buf, ep->req.length);
+ memcpy(ci_req->hw_buf, req->buf, req->length);
flush:
- ba = (uint32_t)ep->b_buf;
- flush_dcache_range(ba, ba + ep->b_len);
+ hwaddr = (uint32_t)ci_req->hw_buf;
+ aligned_used_len = roundup(req->length, ARCH_DMA_MINALIGN);
+ flush_dcache_range(hwaddr, hwaddr + aligned_used_len);
return 0;
}
-static void ci_debounce(struct ci_ep *ep, int in)
+static void ci_debounce(struct ci_req *ci_req, int in)
{
- uint32_t addr = (uint32_t)ep->req.buf;
- uint32_t ba = (uint32_t)ep->b_buf;
+ struct usb_request *req = &ci_req->req;
+ uint32_t addr = (uint32_t)req->buf;
+ uint32_t hwaddr = (uint32_t)ci_req->hw_buf;
+ uint32_t aligned_used_len;
- if (in) {
- if (addr == ba)
- return; /* not a bounce */
- goto free;
- }
- invalidate_dcache_range(ba, ba + ep->b_len);
+ if (in)
+ return;
+
+ aligned_used_len = roundup(req->actual, ARCH_DMA_MINALIGN);
+ invalidate_dcache_range(hwaddr, hwaddr + aligned_used_len);
- if (addr == ba)
- return; /* not a bounce */
+ if (addr == hwaddr)
+ return; /* not a bounce */
- memcpy(ep->req.buf, ep->b_buf, ep->req.actual);
-free:
- /* Large payloads use allocated buffer, free it. */
- if (ep->b_buf != ep->b_fast)
- free(ep->b_buf);
+ memcpy(req->buf, ci_req->hw_buf, req->actual);
}
-static int ci_ep_queue(struct usb_ep *ep,
- struct usb_request *req, gfp_t gfp_flags)
+static void ci_ep_submit_next_request(struct ci_ep *ci_ep)
{
- struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
struct ept_queue_item *item;
struct ept_queue_head *head;
- int bit, num, len, in, ret;
+ int bit, num, len, in;
+ struct ci_req *ci_req;
+
+ ci_ep->req_primed = true;
+
num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
item = ci_get_qtd(num, in);
head = ci_get_qh(num, in);
- len = req->length;
- ret = ci_bounce(ci_ep, in);
- if (ret)
- return ret;
+ ci_req = list_first_entry(&ci_ep->queue, struct ci_req, queue);
+ len = ci_req->req.length;
item->next = TERMINATE;
item->info = INFO_BYTES(len) | INFO_IOC | INFO_ACTIVE;
- item->page0 = (uint32_t)ci_ep->b_buf;
- item->page1 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x1000;
- item->page2 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x2000;
- item->page3 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x3000;
- item->page4 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x4000;
+ item->page0 = (uint32_t)ci_req->hw_buf;
+ item->page1 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x1000;
+ item->page2 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x2000;
+ item->page3 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x3000;
+ item->page4 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x4000;
ci_flush_qtd(num);
head->next = (unsigned) item;
head->info = 0;
- DBG("ept%d %s queue len %x, buffer %p\n",
- num, in ? "in" : "out", len, ci_ep->b_buf);
+ DBG("ept%d %s queue len %x, req %p, buffer %p\n",
+ num, in ? "in" : "out", len, ci_req, ci_req->hw_buf);
ci_flush_qh(num);
if (in)
@@ -368,6 +384,29 @@ static int ci_ep_queue(struct usb_ep *ep,
bit = EPT_RX(num);
writel(bit, &udc->epprime);
+}
+
+static int ci_ep_queue(struct usb_ep *ep,
+ struct usb_request *req, gfp_t gfp_flags)
+{
+ struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
+ struct ci_req *ci_req = container_of(req, struct ci_req, req);
+ int in, ret;
+ int __maybe_unused num;
+
+ num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
+
+ ret = ci_bounce(ci_req, in);
+ if (ret)
+ return ret;
+
+ DBG("ept%d %s pre-queue req %p, buffer %p\n",
+ num, in ? "in" : "out", ci_req, ci_req->hw_buf);
+ list_add_tail(&ci_req->queue, &ci_ep->queue);
+
+ if (!ci_ep->req_primed)
+ ci_ep_submit_next_request(ci_ep);
return 0;
}
@@ -376,6 +415,8 @@ static void handle_ep_complete(struct ci_ep *ep)
{
struct ept_queue_item *item;
int num, in, len;
+ struct ci_req *ci_req;
+
num = ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
in = (ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
if (num == 0)
@@ -383,20 +424,27 @@ static void handle_ep_complete(struct ci_ep *ep)
item = ci_get_qtd(num, in);
ci_invalidate_qtd(num);
+ len = (item->info >> 16) & 0x7fff;
if (item->info & 0xff)
printf("EP%d/%s FAIL info=%x pg0=%x\n",
num, in ? "in" : "out", item->info, item->page0);
- len = (item->info >> 16) & 0x7fff;
- ep->req.actual = ep->req.length - len;
- ci_debounce(ep, in);
+ ci_req = list_first_entry(&ep->queue, struct ci_req, queue);
+ list_del_init(&ci_req->queue);
+ ep->req_primed = false;
+
+ if (!list_empty(&ep->queue))
+ ci_ep_submit_next_request(ep);
+
+ ci_req->req.actual = ci_req->req.length - len;
+ ci_debounce(ci_req, in);
- DBG("ept%d %s complete %x\n",
- num, in ? "in" : "out", len);
- ep->req.complete(&ep->ep, &ep->req);
+ DBG("ept%d %s req %p, complete %x\n",
+ num, in ? "in" : "out", ci_req, len);
+ ci_req->req.complete(&ep->ep, &ci_req->req);
if (num == 0) {
- ep->req.length = 0;
- usb_ep_queue(&ep->ep, &ep->req, 0);
+ ci_req->req.length = 0;
+ usb_ep_queue(&ep->ep, &ci_req->req, 0);
ep->desc = &ep0_in_desc;
}
}
@@ -405,13 +453,18 @@ static void handle_ep_complete(struct ci_ep *ep)
static void handle_setup(void)
{
- struct usb_request *req = &controller.ep[0].req;
+ struct ci_ep *ci_ep = &controller.ep[0];
+ struct ci_req *ci_req;
+ struct usb_request *req;
struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
struct ept_queue_head *head;
struct usb_ctrlrequest r;
int status = 0;
int num, in, _num, _in, i;
char *buf;
+
+ ci_req = list_first_entry(&ci_ep->queue, struct ci_req, queue);
+ req = &ci_req->req;
head = ci_get_qh(0, 0); /* EP0 OUT */
ci_invalidate_qh(0);
@@ -424,6 +477,9 @@ static void handle_setup(void)
DBG("handle setup %s, %x, %x index %x value %x\n", reqname(r.bRequest),
r.bRequestType, r.bRequest, r.wIndex, r.wValue);
+ list_del_init(&ci_req->queue);
+ ci_ep->req_primed = false;
+
switch (SETUP(r.bRequestType, r.bRequest)) {
case SETUP(USB_RECIP_ENDPOINT, USB_REQ_CLEAR_FEATURE):
_num = r.wIndex & 15;
@@ -701,6 +757,8 @@ static int ci_udc_probe(void)
/* Init EP 0 */
memcpy(&controller.ep[0].ep, &ci_ep_init[0], sizeof(*ci_ep_init));
controller.ep[0].desc = &ep0_in_desc;
+ INIT_LIST_HEAD(&controller.ep[0].queue);
+ controller.ep[0].req_primed = false;
controller.gadget.ep0 = &controller.ep[0].ep;
INIT_LIST_HEAD(&controller.gadget.ep0->ep_list);
@@ -708,6 +766,8 @@ static int ci_udc_probe(void)
for (i = 1; i < NUM_ENDPOINTS; i++) {
memcpy(&controller.ep[i].ep, &ci_ep_init[1],
sizeof(*ci_ep_init));
+ INIT_LIST_HEAD(&controller.ep[i].queue);
+ controller.ep[i].req_primed = false;
list_add_tail(&controller.ep[i].ep.ep_list,
&controller.gadget.ep_list);
}
diff --git a/drivers/usb/gadget/ci_udc.h b/drivers/usb/gadget/ci_udc.h
index 4425fd9345..23cff56d7e 100644
--- a/drivers/usb/gadget/ci_udc.h
+++ b/drivers/usb/gadget/ci_udc.h
@@ -77,15 +77,22 @@ struct ci_udc {
#define CTRL_TXT_BULK (2 << 18)
#define CTRL_RXT_BULK (2 << 2)
+struct ci_req {
+ struct usb_request req;
+ struct list_head queue;
+ /* Bounce buffer allocated if needed to align the transfer */
+ uint8_t *b_buf;
+ uint32_t b_len;
+ /* Buffer for the current transfer. Either req.buf/len or b_buf/len */
+ uint8_t *hw_buf;
+ uint32_t hw_len;
+};
+
struct ci_ep {
struct usb_ep ep;
struct list_head queue;
+ bool req_primed;
const struct usb_endpoint_descriptor *desc;
-
- struct usb_request req;
- uint8_t *b_buf;
- uint32_t b_len;
- uint8_t b_fast[64] __aligned(ARCH_DMA_MINALIGN);
};
struct ci_drv {
diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c
index 1b1e1793d9..859fe828de 100644
--- a/drivers/usb/gadget/f_dfu.c
+++ b/drivers/usb/gadget/f_dfu.c
@@ -175,10 +175,17 @@ static void dnload_request_flush(struct usb_ep *ep, struct usb_request *req)
req->length, f_dfu->blk_seq_num);
}
+static inline int dfu_get_manifest_timeout(struct dfu_entity *dfu)
+{
+ return dfu->poll_timeout ? dfu->poll_timeout(dfu) :
+ DFU_MANIFEST_POLL_TIMEOUT;
+}
+
static void handle_getstatus(struct usb_request *req)
{
struct dfu_status *dstat = (struct dfu_status *)req->buf;
struct f_dfu *f_dfu = req->context;
+ struct dfu_entity *dfu = dfu_get_entity(f_dfu->altsetting);
dfu_set_poll_timeout(dstat, 0);
@@ -191,7 +198,8 @@ static void handle_getstatus(struct usb_request *req)
f_dfu->dfu_state = DFU_STATE_dfuMANIFEST;
break;
case DFU_STATE_dfuMANIFEST:
- dfu_set_poll_timeout(dstat, DFU_MANIFEST_POLL_TIMEOUT);
+ dfu_set_poll_timeout(dstat, dfu_get_manifest_timeout(dfu));
+ break;
default:
break;
}
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
new file mode 100644
index 0000000000..9dd85b636e
--- /dev/null
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -0,0 +1,513 @@
+/*
+ * (C) Copyright 2008 - 2009
+ * Windriver, <www.windriver.com>
+ * Tom Rix <Tom.Rix@windriver.com>
+ *
+ * Copyright 2011 Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Copyright 2014 Linaro, Ltd.
+ * Rob Herring <robh@kernel.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#include <common.h>
+#include <errno.h>
+#include <malloc.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+#include <linux/compiler.h>
+#include <version.h>
+#include <g_dnl.h>
+
+#define FASTBOOT_VERSION "0.4"
+
+#define FASTBOOT_INTERFACE_CLASS 0xff
+#define FASTBOOT_INTERFACE_SUB_CLASS 0x42
+#define FASTBOOT_INTERFACE_PROTOCOL 0x03
+
+#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0 (0x0200)
+#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1 (0x0040)
+#define TX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0040)
+
+/* The 64 defined bytes plus \0 */
+#define RESPONSE_LEN (64 + 1)
+
+#define EP_BUFFER_SIZE 4096
+
+struct f_fastboot {
+ struct usb_function usb_function;
+
+ /* IN/OUT EP's and correspoinding requests */
+ struct usb_ep *in_ep, *out_ep;
+ struct usb_request *in_req, *out_req;
+};
+
+static inline struct f_fastboot *func_to_fastboot(struct usb_function *f)
+{
+ return container_of(f, struct f_fastboot, usb_function);
+}
+
+static struct f_fastboot *fastboot_func;
+static unsigned int download_size;
+static unsigned int download_bytes;
+
+static struct usb_endpoint_descriptor fs_ep_in = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = TX_ENDPOINT_MAXIMUM_PACKET_SIZE,
+ .bInterval = 0x00,
+};
+
+static struct usb_endpoint_descriptor fs_ep_out = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1,
+ .bInterval = 0x00,
+};
+
+static struct usb_endpoint_descriptor hs_ep_out = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0,
+ .bInterval = 0x00,
+};
+
+static struct usb_interface_descriptor interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0x00,
+ .bAlternateSetting = 0x00,
+ .bNumEndpoints = 0x02,
+ .bInterfaceClass = FASTBOOT_INTERFACE_CLASS,
+ .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS,
+ .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL,
+};
+
+static struct usb_descriptor_header *fb_runtime_descs[] = {
+ (struct usb_descriptor_header *)&interface_desc,
+ (struct usb_descriptor_header *)&fs_ep_in,
+ (struct usb_descriptor_header *)&hs_ep_out,
+ NULL,
+};
+
+/*
+ * static strings, in UTF-8
+ */
+static const char fastboot_name[] = "Android Fastboot";
+
+static struct usb_string fastboot_string_defs[] = {
+ [0].s = fastboot_name,
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_fastboot = {
+ .language = 0x0409, /* en-us */
+ .strings = fastboot_string_defs,
+};
+
+static struct usb_gadget_strings *fastboot_strings[] = {
+ &stringtab_fastboot,
+ NULL,
+};
+
+static void rx_handler_command(struct usb_ep *ep, struct usb_request *req);
+
+static void fastboot_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ int status = req->status;
+ if (!status)
+ return;
+ printf("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual);
+}
+
+static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ int id;
+ struct usb_gadget *gadget = c->cdev->gadget;
+ struct f_fastboot *f_fb = func_to_fastboot(f);
+
+ /* DYNAMIC interface numbers assignments */
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ interface_desc.bInterfaceNumber = id;
+
+ id = usb_string_id(c->cdev);
+ if (id < 0)
+ return id;
+ fastboot_string_defs[0].id = id;
+ interface_desc.iInterface = id;
+
+ f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in);
+ if (!f_fb->in_ep)
+ return -ENODEV;
+ f_fb->in_ep->driver_data = c->cdev;
+
+ f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out);
+ if (!f_fb->out_ep)
+ return -ENODEV;
+ f_fb->out_ep->driver_data = c->cdev;
+
+ hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
+
+ return 0;
+}
+
+static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ memset(fastboot_func, 0, sizeof(*fastboot_func));
+}
+
+static void fastboot_disable(struct usb_function *f)
+{
+ struct f_fastboot *f_fb = func_to_fastboot(f);
+
+ usb_ep_disable(f_fb->out_ep);
+ usb_ep_disable(f_fb->in_ep);
+
+ if (f_fb->out_req) {
+ free(f_fb->out_req->buf);
+ usb_ep_free_request(f_fb->out_ep, f_fb->out_req);
+ f_fb->out_req = NULL;
+ }
+ if (f_fb->in_req) {
+ free(f_fb->in_req->buf);
+ usb_ep_free_request(f_fb->in_ep, f_fb->in_req);
+ f_fb->in_req = NULL;
+ }
+}
+
+static struct usb_request *fastboot_start_ep(struct usb_ep *ep)
+{
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, 0);
+ if (!req)
+ return NULL;
+
+ req->length = EP_BUFFER_SIZE;
+ req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, EP_BUFFER_SIZE);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+
+ memset(req->buf, 0, req->length);
+ return req;
+}
+
+static int fastboot_set_alt(struct usb_function *f,
+ unsigned interface, unsigned alt)
+{
+ int ret;
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_gadget *gadget = cdev->gadget;
+ struct f_fastboot *f_fb = func_to_fastboot(f);
+
+ debug("%s: func: %s intf: %d alt: %d\n",
+ __func__, f->name, interface, alt);
+
+ /* make sure we don't enable the ep twice */
+ if (gadget->speed == USB_SPEED_HIGH)
+ ret = usb_ep_enable(f_fb->out_ep, &hs_ep_out);
+ else
+ ret = usb_ep_enable(f_fb->out_ep, &fs_ep_out);
+ if (ret) {
+ puts("failed to enable out ep\n");
+ return ret;
+ }
+
+ f_fb->out_req = fastboot_start_ep(f_fb->out_ep);
+ if (!f_fb->out_req) {
+ puts("failed to alloc out req\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ f_fb->out_req->complete = rx_handler_command;
+
+ ret = usb_ep_enable(f_fb->in_ep, &fs_ep_in);
+ if (ret) {
+ puts("failed to enable in ep\n");
+ goto err;
+ }
+
+ f_fb->in_req = fastboot_start_ep(f_fb->in_ep);
+ if (!f_fb->in_req) {
+ puts("failed alloc req in\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ f_fb->in_req->complete = fastboot_complete;
+
+ ret = usb_ep_queue(f_fb->out_ep, f_fb->out_req, 0);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ fastboot_disable(f);
+ return ret;
+}
+
+static int fastboot_add(struct usb_configuration *c)
+{
+ struct f_fastboot *f_fb = fastboot_func;
+ int status;
+
+ debug("%s: cdev: 0x%p\n", __func__, c->cdev);
+
+ if (!f_fb) {
+ f_fb = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_fb));
+ if (!f_fb)
+ return -ENOMEM;
+
+ fastboot_func = f_fb;
+ memset(f_fb, 0, sizeof(*f_fb));
+ }
+
+ f_fb->usb_function.name = "f_fastboot";
+ f_fb->usb_function.hs_descriptors = fb_runtime_descs;
+ f_fb->usb_function.bind = fastboot_bind;
+ f_fb->usb_function.unbind = fastboot_unbind;
+ f_fb->usb_function.set_alt = fastboot_set_alt;
+ f_fb->usb_function.disable = fastboot_disable;
+ f_fb->usb_function.strings = fastboot_strings;
+
+ status = usb_add_function(c, &f_fb->usb_function);
+ if (status) {
+ free(f_fb);
+ fastboot_func = f_fb;
+ }
+
+ return status;
+}
+DECLARE_GADGET_BIND_CALLBACK(usb_dnl_fastboot, fastboot_add);
+
+int fastboot_tx_write(const char *buffer, unsigned int buffer_size)
+{
+ struct usb_request *in_req = fastboot_func->in_req;
+ int ret;
+
+ memcpy(in_req->buf, buffer, buffer_size);
+ in_req->length = buffer_size;
+ ret = usb_ep_queue(fastboot_func->in_ep, in_req, 0);
+ if (ret)
+ printf("Error %d on queue\n", ret);
+ return 0;
+}
+
+static int fastboot_tx_write_str(const char *buffer)
+{
+ return fastboot_tx_write(buffer, strlen(buffer));
+}
+
+static void compl_do_reset(struct usb_ep *ep, struct usb_request *req)
+{
+ do_reset(NULL, 0, 0, NULL);
+}
+
+static void cb_reboot(struct usb_ep *ep, struct usb_request *req)
+{
+ fastboot_func->in_req->complete = compl_do_reset;
+ fastboot_tx_write_str("OKAY");
+}
+
+static int strcmp_l1(const char *s1, const char *s2)
+{
+ if (!s1 || !s2)
+ return -1;
+ return strncmp(s1, s2, strlen(s1));
+}
+
+static void cb_getvar(struct usb_ep *ep, struct usb_request *req)
+{
+ char *cmd = req->buf;
+ char response[RESPONSE_LEN];
+ const char *s;
+
+ strcpy(response, "OKAY");
+ strsep(&cmd, ":");
+ if (!cmd) {
+ fastboot_tx_write_str("FAILmissing var");
+ return;
+ }
+
+ if (!strcmp_l1("version", cmd)) {
+ strncat(response, FASTBOOT_VERSION, sizeof(response));
+ } else if (!strcmp_l1("bootloader-version", cmd)) {
+ strncat(response, U_BOOT_VERSION, sizeof(response));
+ } else if (!strcmp_l1("downloadsize", cmd)) {
+ char str_num[12];
+
+ sprintf(str_num, "%08x", CONFIG_USB_FASTBOOT_BUF_SIZE);
+ strncat(response, str_num, sizeof(response));
+ } else if (!strcmp_l1("serialno", cmd)) {
+ s = getenv("serial#");
+ if (s)
+ strncat(response, s, sizeof(response));
+ else
+ strcpy(response, "FAILValue not set");
+ } else {
+ strcpy(response, "FAILVariable not implemented");
+ }
+ fastboot_tx_write_str(response);
+}
+
+static unsigned int rx_bytes_expected(void)
+{
+ int rx_remain = download_size - download_bytes;
+ if (rx_remain < 0)
+ return 0;
+ if (rx_remain > EP_BUFFER_SIZE)
+ return EP_BUFFER_SIZE;
+ return rx_remain;
+}
+
+#define BYTES_PER_DOT 0x20000
+static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req)
+{
+ char response[RESPONSE_LEN];
+ unsigned int transfer_size = download_size - download_bytes;
+ const unsigned char *buffer = req->buf;
+ unsigned int buffer_size = req->actual;
+
+ if (req->status != 0) {
+ printf("Bad status: %d\n", req->status);
+ return;
+ }
+
+ if (buffer_size < transfer_size)
+ transfer_size = buffer_size;
+
+ memcpy((void *)CONFIG_USB_FASTBOOT_BUF_ADDR + download_bytes,
+ buffer, transfer_size);
+
+ download_bytes += transfer_size;
+
+ /* Check if transfer is done */
+ if (download_bytes >= download_size) {
+ /*
+ * Reset global transfer variable, keep download_bytes because
+ * it will be used in the next possible flashing command
+ */
+ download_size = 0;
+ req->complete = rx_handler_command;
+ req->length = EP_BUFFER_SIZE;
+
+ sprintf(response, "OKAY");
+ fastboot_tx_write_str(response);
+
+ printf("\ndownloading of %d bytes finished\n", download_bytes);
+ } else {
+ req->length = rx_bytes_expected();
+ if (req->length < ep->maxpacket)
+ req->length = ep->maxpacket;
+ }
+
+ if (download_bytes && !(download_bytes % BYTES_PER_DOT)) {
+ putc('.');
+ if (!(download_bytes % (74 * BYTES_PER_DOT)))
+ putc('\n');
+ }
+ req->actual = 0;
+ usb_ep_queue(ep, req, 0);
+}
+
+static void cb_download(struct usb_ep *ep, struct usb_request *req)
+{
+ char *cmd = req->buf;
+ char response[RESPONSE_LEN];
+
+ strsep(&cmd, ":");
+ download_size = simple_strtoul(cmd, NULL, 16);
+ download_bytes = 0;
+
+ printf("Starting download of %d bytes\n", download_size);
+
+ if (0 == download_size) {
+ sprintf(response, "FAILdata invalid size");
+ } else if (download_size > CONFIG_USB_FASTBOOT_BUF_SIZE) {
+ download_size = 0;
+ sprintf(response, "FAILdata too large");
+ } else {
+ sprintf(response, "DATA%08x", download_size);
+ req->complete = rx_handler_dl_image;
+ req->length = rx_bytes_expected();
+ if (req->length < ep->maxpacket)
+ req->length = ep->maxpacket;
+ }
+ fastboot_tx_write_str(response);
+}
+
+static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ char boot_addr_start[12];
+ char *bootm_args[] = { "bootm", boot_addr_start, NULL };
+
+ puts("Booting kernel..\n");
+
+ sprintf(boot_addr_start, "0x%lx", load_addr);
+ do_bootm(NULL, 0, 2, bootm_args);
+
+ /* This only happens if image is somehow faulty so we start over */
+ do_reset(NULL, 0, 0, NULL);
+}
+
+static void cb_boot(struct usb_ep *ep, struct usb_request *req)
+{
+ fastboot_func->in_req->complete = do_bootm_on_complete;
+ fastboot_tx_write_str("OKAY");
+}
+
+struct cmd_dispatch_info {
+ char *cmd;
+ void (*cb)(struct usb_ep *ep, struct usb_request *req);
+};
+
+static const struct cmd_dispatch_info cmd_dispatch_info[] = {
+ {
+ .cmd = "reboot",
+ .cb = cb_reboot,
+ }, {
+ .cmd = "getvar:",
+ .cb = cb_getvar,
+ }, {
+ .cmd = "download:",
+ .cb = cb_download,
+ }, {
+ .cmd = "boot",
+ .cb = cb_boot,
+ },
+};
+
+static void rx_handler_command(struct usb_ep *ep, struct usb_request *req)
+{
+ char *cmdbuf = req->buf;
+ void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) {
+ if (!strcmp_l1(cmd_dispatch_info[i].cmd, cmdbuf)) {
+ func_cb = cmd_dispatch_info[i].cb;
+ break;
+ }
+ }
+
+ if (!func_cb)
+ fastboot_tx_write_str("FAILunknown command");
+ else
+ func_cb(ep, req);
+
+ if (req->status == 0) {
+ *cmdbuf = '\0';
+ req->actual = 0;
+ usb_ep_queue(ep, req, 0);
+ }
+}
diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c
index feef9e4619..28f215e07c 100644
--- a/drivers/usb/gadget/f_thor.c
+++ b/drivers/usb/gadget/f_thor.c
@@ -219,21 +219,15 @@ static int download_tail(long long int left, int cnt)
}
/*
- * To store last "packet" DFU storage backend requires dfu_write with
- * size parameter equal to 0
+ * To store last "packet" or write file from buffer to filesystem
+ * DFU storage backend requires dfu_flush
*
* This also frees memory malloc'ed by dfu_get_buf(), so no explicit
* need fo call dfu_free_buf() is needed.
*/
- ret = dfu_write(dfu_entity, transfer_buffer, 0, cnt);
- if (ret)
- error("DFU write failed [%d] cnt: %d", ret, cnt);
-
ret = dfu_flush(dfu_entity, transfer_buffer, 0, cnt);
- if (ret) {
+ if (ret)
error("DFU flush failed!");
- return ret;
- }
return ret;
}
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index 74300746b9..02803df23c 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -311,11 +311,7 @@ static struct fsg_lun *fsg_lun_from_dev(struct device *dev)
#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */
/* Number of buffers we will use. 2 is enough for double-buffering */
-#ifndef CONFIG_CI_UDC
#define FSG_NUM_BUFFERS 2
-#else
-#define FSG_NUM_BUFFERS 1 /* ci_udc only allows 1 req per ep at present */
-#endif
/* Default size of buffer length. */
#define FSG_BUFLEN ((u32)16384)
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 38db18e2c9..33e5ea9ebd 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -69,6 +69,7 @@ struct fdt_usb {
unsigned enabled:1; /* 1 to enable, 0 to disable */
unsigned has_legacy_mode:1; /* 1 if this port has legacy mode */
unsigned initialized:1; /* has this port already been initialized? */
+ enum usb_init_type init_type;
enum dr_mode dr_mode; /* dual role mode */
enum periph_id periph_id;/* peripheral id */
struct fdt_gpio_state vbus_gpio; /* GPIO for vbus enable */
@@ -237,29 +238,31 @@ int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg)
return PORTSC_PSPD(reg);
}
-/* Put the port into host mode */
-static void set_host_mode(struct fdt_usb *config)
+/* Set up VBUS for host/device mode */
+static void set_up_vbus(struct fdt_usb *config, enum usb_init_type init)
{
/*
- * If we are an OTG port, check if remote host is driving VBus and
- * bail out in this case.
+ * If we are an OTG port initializing in host mode,
+ * check if remote host is driving VBus and bail out in this case.
*/
- if (config->dr_mode == DR_MODE_OTG &&
- (readl(&config->reg->phy_vbus_sensors) & VBUS_VLD_STS))
+ if (init == USB_INIT_HOST &&
+ config->dr_mode == DR_MODE_OTG &&
+ (readl(&config->reg->phy_vbus_sensors) & VBUS_VLD_STS)) {
+ printf("tegrausb: VBUS input active; not enabling as host\n");
return;
+ }
- /*
- * If not driving, we set the GPIO to enable VBUS. We assume
- * that the pinmux is set up correctly for this.
- */
if (fdt_gpio_isvalid(&config->vbus_gpio)) {
+ int vbus_value;
+
fdtdec_setup_gpio(&config->vbus_gpio);
- gpio_direction_output(config->vbus_gpio.gpio,
- (config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW) ?
- 0 : 1);
- debug("set_host_mode: GPIO %d %s\n", config->vbus_gpio.gpio,
- (config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW) ?
- "low" : "high");
+
+ vbus_value = (init == USB_INIT_HOST) ^
+ !!(config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW);
+ gpio_direction_output(config->vbus_gpio.gpio, vbus_value);
+
+ debug("set_up_vbus: GPIO %d %d\n", config->vbus_gpio.gpio,
+ vbus_value);
}
}
@@ -293,10 +296,44 @@ static const unsigned *get_pll_timing(void)
return timing;
}
+/* select the PHY to use with a USB controller */
+static void init_phy_mux(struct fdt_usb *config, uint pts,
+ enum usb_init_type init)
+{
+ struct usb_ctlr *usbctlr = config->reg;
+
+#if defined(CONFIG_TEGRA20)
+ if (config->periph_id == PERIPH_ID_USBD) {
+ clrsetbits_le32(&usbctlr->port_sc1, PTS1_MASK,
+ PTS_UTMI << PTS1_SHIFT);
+ clrbits_le32(&usbctlr->port_sc1, STS1);
+ } else {
+ clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK,
+ PTS_UTMI << PTS_SHIFT);
+ clrbits_le32(&usbctlr->port_sc1, STS);
+ }
+#else
+ /* Set to Host mode (if applicable) after Controller Reset was done */
+ clrsetbits_le32(&usbctlr->usb_mode, USBMODE_CM_HC,
+ (init == USB_INIT_HOST) ? USBMODE_CM_HC : 0);
+ /*
+ * Select PHY interface after setting host mode.
+ * For device mode, the ordering requirement is not an issue, since
+ * only the first USB controller supports device mode, and that USB
+ * controller can only talk to a UTMI PHY, so the PHY selection is
+ * already made at reset time, so this write is a no-op.
+ */
+ clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK,
+ pts << PTS_SHIFT);
+ clrbits_le32(&usbctlr->hostpc1_devlc, STS);
+#endif
+}
+
/* set up the UTMI USB controller with the parameters provided */
-static int init_utmi_usb_controller(struct fdt_usb *config)
+static int init_utmi_usb_controller(struct fdt_usb *config,
+ enum usb_init_type init)
{
- u32 val;
+ u32 b_sess_valid_mask, val;
int loop_count;
const unsigned *timing;
struct usb_ctlr *usbctlr = config->reg;
@@ -314,6 +351,10 @@ static int init_utmi_usb_controller(struct fdt_usb *config)
/* Follow the crystal clock disable by >100ns delay */
udelay(1);
+ b_sess_valid_mask = (VBUS_B_SESS_VLD_SW_VALUE | VBUS_B_SESS_VLD_SW_EN);
+ clrsetbits_le32(&usbctlr->phy_vbus_sensors, b_sess_valid_mask,
+ (init == USB_INIT_DEVICE) ? b_sess_valid_mask : 0);
+
/*
* To Use the A Session Valid for cable detection logic, VBUS_WAKEUP
* mux must be switched to actually use a_sess_vld threshold.
@@ -485,21 +526,7 @@ static int init_utmi_usb_controller(struct fdt_usb *config)
clrbits_le32(&usbctlr->icusb_ctrl, IC_ENB1);
/* Select UTMI parallel interface */
-#if defined(CONFIG_TEGRA20)
- if (config->periph_id == PERIPH_ID_USBD) {
- clrsetbits_le32(&usbctlr->port_sc1, PTS1_MASK,
- PTS_UTMI << PTS1_SHIFT);
- clrbits_le32(&usbctlr->port_sc1, STS1);
- } else {
- clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK,
- PTS_UTMI << PTS_SHIFT);
- clrbits_le32(&usbctlr->port_sc1, STS);
- }
-#else
- clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK,
- PTS_UTMI << PTS_SHIFT);
- clrbits_le32(&usbctlr->hostpc1_devlc, STS);
-#endif
+ init_phy_mux(config, PTS_UTMI, init);
/* Deassert power down state */
clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_FORCE_PD_POWERDOWN |
@@ -529,7 +556,8 @@ static int init_utmi_usb_controller(struct fdt_usb *config)
#endif
/* set up the ULPI USB controller with the parameters provided */
-static int init_ulpi_usb_controller(struct fdt_usb *config)
+static int init_ulpi_usb_controller(struct fdt_usb *config,
+ enum usb_init_type init)
{
u32 val;
int loop_count;
@@ -557,13 +585,7 @@ static int init_ulpi_usb_controller(struct fdt_usb *config)
ULPI_CLKOUT_PINMUX_BYP | ULPI_OUTPUT_PINMUX_BYP);
/* Select ULPI parallel interface */
-#if defined(CONFIG_TEGRA20)
- clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK,
- PTS_ULPI << PTS_SHIFT);
-#else
- clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK,
- PTS_ULPI << PTS_SHIFT);
-#endif
+ init_phy_mux(config, PTS_ULPI, init);
/* enable ULPI transceiver */
setbits_le32(&usbctlr->susp_ctrl, ULPI_PHY_ENB);
@@ -612,7 +634,8 @@ static int init_ulpi_usb_controller(struct fdt_usb *config)
return 0;
}
#else
-static int init_ulpi_usb_controller(struct fdt_usb *config)
+static int init_ulpi_usb_controller(struct fdt_usb *config,
+ enum usb_init_type init)
{
printf("No code to set up ULPI controller, please enable"
"CONFIG_USB_ULPI and CONFIG_USB_ULPI_VIEWPORT");
@@ -765,42 +788,66 @@ int ehci_hcd_init(int index, enum usb_init_type init,
config = &port[index];
+ switch (init) {
+ case USB_INIT_HOST:
+ switch (config->dr_mode) {
+ case DR_MODE_HOST:
+ case DR_MODE_OTG:
+ break;
+ default:
+ printf("tegrausb: Invalid dr_mode %d for host mode\n",
+ config->dr_mode);
+ return -1;
+ }
+ break;
+ case USB_INIT_DEVICE:
+ if (config->periph_id != PERIPH_ID_USBD) {
+ printf("tegrausb: Device mode only supported on first USB controller\n");
+ return -1;
+ }
+ if (!config->utmi) {
+ printf("tegrausb: Device mode only supported with UTMI PHY\n");
+ return -1;
+ }
+ switch (config->dr_mode) {
+ case DR_MODE_DEVICE:
+ case DR_MODE_OTG:
+ break;
+ default:
+ printf("tegrausb: Invalid dr_mode %d for device mode\n",
+ config->dr_mode);
+ return -1;
+ }
+ break;
+ default:
+ printf("tegrausb: Unknown USB_INIT_* %d\n", init);
+ return -1;
+ }
+
/* skip init, if the port is already initialized */
- if (config->initialized)
+ if (config->initialized && config->init_type == init)
goto success;
- if (config->utmi && init_utmi_usb_controller(config)) {
+ if (config->utmi && init_utmi_usb_controller(config, init)) {
printf("tegrausb: Cannot init port %d\n", index);
return -1;
}
- if (config->ulpi && init_ulpi_usb_controller(config)) {
+ if (config->ulpi && init_ulpi_usb_controller(config, init)) {
printf("tegrausb: Cannot init port %d\n", index);
return -1;
}
- set_host_mode(config);
+ set_up_vbus(config, init);
config->initialized = 1;
+ config->init_type = init;
success:
usbctlr = config->reg;
*hccr = (struct ehci_hccr *)&usbctlr->cap_length;
*hcor = (struct ehci_hcor *)&usbctlr->usb_cmd;
- if (controller->has_hostpc) {
- /* Set to Host mode after Controller Reset was done */
- clrsetbits_le32(&usbctlr->usb_mode, USBMODE_CM_HC,
- USBMODE_CM_HC);
- /* Select UTMI parallel interface after setting host mode */
- if (config->utmi) {
- clrsetbits_le32((char *)&usbctlr->usb_cmd +
- HOSTPC1_DEVLC, PTS_MASK,
- PTS_UTMI << PTS_SHIFT);
- clrbits_le32((char *)&usbctlr->usb_cmd +
- HOSTPC1_DEVLC, STS);
- }
- }
return 0;
}
diff --git a/drivers/usb/musb-new/musb_gadget_ep0.c b/drivers/usb/musb-new/musb_gadget_ep0.c
index 6599d386dc..8c3b0a145a 100644
--- a/drivers/usb/musb-new/musb_gadget_ep0.c
+++ b/drivers/usb/musb-new/musb_gadget_ep0.c
@@ -576,6 +576,10 @@ static void ep0_txstate(struct musb *musb)
} else
request = NULL;
+ /* send it out, triggering a "txpktrdy cleared" irq */
+ musb_ep_select(musb->mregs, 0);
+ musb_writew(regs, MUSB_CSR0, csr);
+
/* report completions as soon as the fifo's loaded; there's no
* win in waiting till this last packet gets acked. (other than
* very precise fault reporting, needed by USB TMC; possible with
@@ -588,10 +592,6 @@ static void ep0_txstate(struct musb *musb)
return;
musb->ackpend = 0;
}
-
- /* send it out, triggering a "txpktrdy cleared" irq */
- musb_ep_select(musb->mregs, 0);
- musb_writew(regs, MUSB_CSR0, csr);
}
/*