Commit 61e76f53 authored by Tom Rini's avatar Tom Rini

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

parents 76b21026 3d83e675
......@@ -1424,9 +1424,6 @@ The following options need to be configured:
CONFIG_USB_EHCI_TXFIFO_THRESH enables setting of the
txfilltuning field in the EHCI controller on reset.
CONFIG_USB_HUB_MIN_POWER_ON_DELAY defines the minimum
interval for usb hub power-on delay.(minimum 100msec)
- USB Device:
Define the below if you wish to use the USB console.
Once firmware is rebuilt from a serial console issue the
......
......@@ -35,10 +35,6 @@
#include <asm/4xx_pci.h>
#endif
#ifndef CONFIG_USB_HUB_MIN_POWER_ON_DELAY
#define CONFIG_USB_HUB_MIN_POWER_ON_DELAY 100
#endif
#define USB_BUFSIZ 512
static struct usb_hub_device hub_dev[USB_MAX_HUB];
......@@ -138,8 +134,11 @@ static void usb_hub_power_on(struct usb_hub_device *hub)
debug("port %d returns %lX\n", i + 1, dev->status);
}
/* Wait for power to become stable */
mdelay(max(pgood_delay, CONFIG_USB_HUB_MIN_POWER_ON_DELAY));
/*
* Wait for power to become stable,
* plus spec-defined max time for device to connect
*/
mdelay(pgood_delay + 1000);
}
void usb_hub_reset(void)
......
......@@ -13,6 +13,7 @@
#include <mmc.h>
#include <fat.h>
#include <dfu.h>
#include <hash.h>
#include <linux/list.h>
#include <linux/compiler.h>
......@@ -20,6 +21,7 @@ static bool dfu_reset_request;
static LIST_HEAD(dfu_list);
static int dfu_alt_num;
static int alt_num_cnt;
static struct hash_algo *dfu_hash_algo;
bool dfu_reset(void)
{
......@@ -99,6 +101,23 @@ unsigned char *dfu_get_buf(void)
return dfu_buf;
}
static char *dfu_get_hash_algo(void)
{
char *s;
s = getenv("dfu_hash_algo");
if (!s)
return NULL;
if (!strcmp(s, "crc32")) {
debug("%s: DFU hash method: %s\n", __func__, s);
return s;
}
error("DFU hash method: %s not supported!\n", s);
return NULL;
}
static int dfu_write_buffer_drain(struct dfu_entity *dfu)
{
long w_size;
......@@ -109,8 +128,9 @@ static int dfu_write_buffer_drain(struct dfu_entity *dfu)
if (w_size == 0)
return 0;
/* update CRC32 */
dfu->crc = crc32(dfu->crc, dfu->i_buf_start, w_size);
if (dfu_hash_algo)
dfu_hash_algo->hash_update(dfu_hash_algo, &dfu->crc,
dfu->i_buf_start, w_size, 0);
ret = dfu->write_medium(dfu, dfu->offset, dfu->i_buf_start, &w_size);
if (ret)
......@@ -138,7 +158,9 @@ int dfu_flush(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
if (dfu->flush_medium)
ret = dfu->flush_medium(dfu);
printf("\nDFU complete CRC32: 0x%08x\n", dfu->crc);
if (dfu_hash_algo)
printf("\nDFU complete %s: 0x%08x\n", dfu_hash_algo->name,
dfu->crc);
/* clear everything */
dfu_free_buf();
......@@ -238,7 +260,11 @@ static int dfu_read_buffer_fill(struct dfu_entity *dfu, void *buf, int size)
/* consume */
if (chunk > 0) {
memcpy(buf, dfu->i_buf, chunk);
dfu->crc = crc32(dfu->crc, buf, chunk);
if (dfu_hash_algo)
dfu_hash_algo->hash_update(dfu_hash_algo,
&dfu->crc, buf,
chunk, 0);
dfu->i_buf += chunk;
dfu->b_left -= chunk;
dfu->r_left -= chunk;
......@@ -322,7 +348,9 @@ int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
}
if (ret < size) {
debug("%s: %s CRC32: 0x%x\n", __func__, dfu->name, dfu->crc);
if (dfu_hash_algo)
debug("%s: %s %s: 0x%x\n", __func__, dfu->name,
dfu_hash_algo->name, dfu->crc);
puts("\nUPLOAD ... done\nCtrl+C to exit ...\n");
dfu_free_buf();
......@@ -397,6 +425,14 @@ int dfu_config_entities(char *env, char *interface, int num)
dfu_alt_num = dfu_find_alt_num(env);
debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num);
dfu_hash_algo = NULL;
s = dfu_get_hash_algo();
if (s) {
ret = hash_lookup_algo(s, &dfu_hash_algo);
if (ret)
error("Hash algorithm %s not supported\n", s);
}
dfu = calloc(sizeof(*dfu), dfu_alt_num);
if (!dfu)
return -1;
......
......@@ -56,14 +56,7 @@ static const char *reqname(unsigned r)
}
#endif
static struct usb_endpoint_descriptor ep0_out_desc = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0,
.bmAttributes = USB_ENDPOINT_XFER_CONTROL,
};
static struct usb_endpoint_descriptor ep0_in_desc = {
static struct usb_endpoint_descriptor ep0_desc = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
......@@ -205,8 +198,14 @@ 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);
int num;
struct ci_req *ci_req;
num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
if (num == 0 && controller.ep0_req)
return &controller.ep0_req->req;
ci_req = memalign(ARCH_DMA_MINALIGN, sizeof(*ci_req));
if (!ci_req)
return NULL;
......@@ -214,14 +213,22 @@ ci_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags)
INIT_LIST_HEAD(&ci_req->queue);
ci_req->b_buf = 0;
if (num == 0)
controller.ep0_req = ci_req;
return &ci_req->req;
}
static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *req)
{
struct ci_req *ci_req;
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 num;
num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
if (num == 0)
controller.ep0_req = 0;
ci_req = container_of(req, struct ci_req, req);
if (ci_req->b_buf)
free(ci_req->b_buf);
free(ci_req);
......@@ -362,18 +369,50 @@ static void ci_ep_submit_next_request(struct ci_ep *ci_ep)
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->info = INFO_BYTES(len) | INFO_ACTIVE;
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;
/*
* When sending the data for an IN transaction, the attached host
* knows that all data for the IN is sent when one of the following
* occurs:
* a) A zero-length packet is transmitted.
* b) A packet with length that isn't an exact multiple of the ep's
* maxpacket is transmitted.
* c) Enough data is sent to exactly fill the host's maximum expected
* IN transaction size.
*
* One of these conditions MUST apply at the end of an IN transaction,
* or the transaction will not be considered complete by the host. If
* none of (a)..(c) already applies, then we must force (a) to apply
* by explicitly sending an extra zero-length packet.
*/
/* IN !a !b !c */
if (in && len && !(len % ci_ep->ep.maxpacket) && ci_req->req.zero) {
/*
* Each endpoint has 2 items allocated, even though typically
* only 1 is used at a time since either an IN or an OUT but
* not both is queued. For an IN transaction, item currently
* points at the second of these items, so we know that we
* can use (item - 1) to transmit the extra zero-length packet
*/
item->next = (unsigned)(item - 1);
item--;
item->info = INFO_ACTIVE;
}
item->next = TERMINATE;
item->info |= INFO_IOC;
ci_flush_qtd(num);
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);
......@@ -397,6 +436,21 @@ static int ci_ep_queue(struct usb_ep *ep,
num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
if (!num && ci_ep->req_primed) {
/*
* The flipping of ep0 between IN and OUT relies on
* ci_ep_queue consuming the current IN/OUT setting
* immediately. If this is deferred to a later point when the
* req is pulled out of ci_req->queue, then the IN/OUT setting
* may have been changed since the req was queued, and state
* will get out of sync. This condition doesn't occur today,
* but could if bugs were introduced later, and this error
* check will save a lot of debugging time.
*/
printf("%s: ep0 transaction already in progress\n", __func__);
return -EPROTO;
}
ret = ci_bounce(ci_req, in);
if (ret)
return ret;
......@@ -411,6 +465,17 @@ static int ci_ep_queue(struct usb_ep *ep,
return 0;
}
static void flip_ep0_direction(void)
{
if (ep0_desc.bEndpointAddress == USB_DIR_IN) {
DBG("%s: Flipping ep0 ot OUT\n", __func__);
ep0_desc.bEndpointAddress = 0;
} else {
DBG("%s: Flipping ep0 ot IN\n", __func__);
ep0_desc.bEndpointAddress = USB_DIR_IN;
}
}
static void handle_ep_complete(struct ci_ep *ep)
{
struct ept_queue_item *item;
......@@ -419,8 +484,6 @@ static void handle_ep_complete(struct ci_ep *ep)
num = ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
in = (ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
if (num == 0)
ep->desc = &ep0_out_desc;
item = ci_get_qtd(num, in);
ci_invalidate_qtd(num);
......@@ -441,11 +504,18 @@ static void handle_ep_complete(struct ci_ep *ep)
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) {
if (num != 0 || controller.ep0_data_phase)
ci_req->req.complete(&ep->ep, &ci_req->req);
if (num == 0 && controller.ep0_data_phase) {
/*
* Data Stage is complete, so flip ep0 dir for Status Stage,
* which always transfers a packet in the opposite direction.
*/
DBG("%s: flip ep0 dir for Status Stage\n", __func__);
flip_ep0_direction();
controller.ep0_data_phase = false;
ci_req->req.length = 0;
usb_ep_queue(&ep->ep, &ci_req->req, 0);
ep->desc = &ep0_in_desc;
}
}
......@@ -463,7 +533,7 @@ static void handle_setup(void)
int num, in, _num, _in, i;
char *buf;
ci_req = list_first_entry(&ci_ep->queue, struct ci_req, queue);
ci_req = controller.ep0_req;
req = &ci_req->req;
head = ci_get_qh(0, 0); /* EP0 OUT */
......@@ -474,8 +544,26 @@ static void handle_setup(void)
#else
writel(EPT_RX(0), &udc->epstat);
#endif
DBG("handle setup %s, %x, %x index %x value %x\n", reqname(r.bRequest),
r.bRequestType, r.bRequest, r.wIndex, r.wValue);
DBG("handle setup %s, %x, %x index %x value %x length %x\n",
reqname(r.bRequest), r.bRequestType, r.bRequest, r.wIndex,
r.wValue, r.wLength);
/* Set EP0 dir for Data Stage based on Setup Stage data */
if (r.bRequestType & USB_DIR_IN) {
DBG("%s: Set ep0 to IN for Data Stage\n", __func__);
ep0_desc.bEndpointAddress = USB_DIR_IN;
} else {
DBG("%s: Set ep0 to OUT for Data Stage\n", __func__);
ep0_desc.bEndpointAddress = 0;
}
if (r.wLength) {
controller.ep0_data_phase = true;
} else {
/* 0 length -> no Data Stage. Flip dir for Status Stage */
DBG("%s: 0 length: flip ep0 dir for Status Stage\n", __func__);
flip_ep0_direction();
controller.ep0_data_phase = false;
}
list_del_init(&ci_req->queue);
ci_ep->req_primed = false;
......@@ -646,6 +734,17 @@ int usb_gadget_handle_interrupts(void)
return value;
}
void udc_disconnect(void)
{
struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
/* disable pullup */
stop_activity();
writel(USBCMD_FS2, &udc->usbcmd);
udelay(800);
if (controller.driver)
controller.driver->disconnect(&controller.gadget);
}
static int ci_pullup(struct usb_gadget *gadget, int is_on)
{
struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
......@@ -664,27 +763,12 @@ static int ci_pullup(struct usb_gadget *gadget, int is_on)
/* Turn on the USB connection by enabling the pullup resistor */
writel(USBCMD_ITC(MICRO_8FRAME) | USBCMD_RUN, &udc->usbcmd);
} else {
stop_activity();
writel(USBCMD_FS2, &udc->usbcmd);
udelay(800);
if (controller.driver)
controller.driver->disconnect(gadget);
udc_disconnect();
}
return 0;
}
void udc_disconnect(void)
{
struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
/* disable pullup */
stop_activity();
writel(USBCMD_FS2, &udc->usbcmd);
udelay(800);
if (controller.driver)
controller.driver->disconnect(&controller.gadget);
}
static int ci_udc_probe(void)
{
struct ept_queue_head *head;
......@@ -756,7 +840,7 @@ 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;
controller.ep[0].desc = &ep0_desc;
INIT_LIST_HEAD(&controller.ep[0].queue);
controller.ep[0].req_primed = false;
controller.gadget.ep0 = &controller.ep[0].ep;
......@@ -772,6 +856,13 @@ static int ci_udc_probe(void)
&controller.gadget.ep_list);
}
ci_ep_alloc_request(&controller.ep[0].ep, 0);
if (!controller.ep0_req) {
free(controller.items_mem);
free(controller.epts);
return -ENOMEM;
}
return 0;
}
......@@ -816,5 +907,11 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
udc_disconnect();
ci_ep_free_request(&controller.ep[0].ep, &controller.ep0_req->req);
free(controller.items_mem);
free(controller.epts);
return 0;
}
......@@ -97,6 +97,8 @@ struct ci_ep {
struct ci_drv {
struct usb_gadget gadget;
struct ci_req *ep0_req;
bool ep0_data_phase;
struct usb_gadget_driver *driver;
struct ehci_ctrl *ctrl;
struct ept_queue_head *epts;
......
......@@ -104,8 +104,6 @@
#define CONFIG_USB_DEVICE
#define CONFIG_USB_TTY
#define CONFIG_SYS_CONSOLE_IS_IN_ENV
/* This delay is really for slow-to-power-on USB sticks, not the hub */
#define CONFIG_USB_HUB_MIN_POWER_ON_DELAY 500
/* commands to include */
#include <config_cmd_default.h>
......
......@@ -192,7 +192,6 @@
#define CONFIG_USB_ETH_CDC
#define CONFIG_NETCONSOLE
#define CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP
#define CONFIG_USB_HUB_MIN_POWER_ON_DELAY 1200
/* Framebuffer and LCD */
#define CONFIG_VIDEO
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment