From e1417f5846b37508e8206fc7170223e140241005 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 27 Mar 2009 12:52:43 -0700 Subject: musb_gadget: fix unhandled endpoint 0 IRQs The gadget EP0 code routinely ignores an interrupt at end of the data phase because of musb_g_ep0_giveback() resetting the state machine to "idle, waiting for SETUP" phase prematurely. The driver also prematurely leaves the status phase on receiving the SetupEnd interrupt. As there were still unhandled endpoint 0 interrupts happening from time to time after fixing these issues, there turned to be yet another culprit: two distinct gadget states collapsed into one. The (missing) state that comes after STATUS IN/OUT states was typically indiscernible from them since the corresponding interrupts tend to happen within too little period of time (due to only a zero-length status packet in between) and so they got coalesced; yet this state is not the same as the next one which is associated with the reception of a SETUP packet. Adding this extra state seems to have fixed the rest of the unhandled interrupts that generic_interrupt() and davinci_interrupt() hid by faking their result and only emitting a debug message -- so, stop doing that. Signed-off-by: Sergei Shtylyov Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/davinci.c | 7 +----- drivers/usb/musb/musb_core.c | 8 +------ drivers/usb/musb/musb_core.h | 3 ++- drivers/usb/musb/musb_gadget_ep0.c | 45 +++++++++++++++++++++++++++++++++----- 4 files changed, 43 insertions(+), 20 deletions(-) diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 10d11ab113ab..898b52fcff5d 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -372,12 +372,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci) spin_unlock_irqrestore(&musb->lock, flags); - /* REVISIT we sometimes get unhandled IRQs - * (e.g. ep0). not clear why... - */ - if (retval != IRQ_HANDLED) - DBG(5, "unhandled? %08x\n", tmp); - return IRQ_HANDLED; + return retval; } int musb_platform_set_mode(struct musb *musb, u8 mode) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 4000cf6d1e81..324459b619f7 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1481,13 +1481,7 @@ static irqreturn_t generic_interrupt(int irq, void *__hci) spin_unlock_irqrestore(&musb->lock, flags); - /* REVISIT we sometimes get spurious IRQs on g_ep0 - * not clear why... - */ - if (retval != IRQ_HANDLED) - DBG(5, "spurious?\n"); - - return IRQ_HANDLED; + return retval; } #else diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index efb39b5e55b5..c2a776ee1947 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -171,7 +171,8 @@ enum musb_h_ep0_state { /* peripheral side ep0 states */ enum musb_g_ep0_state { - MUSB_EP0_STAGE_SETUP, /* idle, waiting for setup */ + MUSB_EP0_STAGE_IDLE, /* idle, waiting for SETUP */ + MUSB_EP0_STAGE_SETUP, /* received SETUP */ MUSB_EP0_STAGE_TX, /* IN data */ MUSB_EP0_STAGE_RX, /* OUT data */ MUSB_EP0_STAGE_STATUSIN, /* (after OUT data) */ diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 3f5e30ddfa27..40ed50ecedff 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -4,6 +4,7 @@ * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation + * Copyright (C) 2008-2009 MontaVista Software, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,7 +59,8 @@ static char *decode_ep0stage(u8 stage) { switch (stage) { - case MUSB_EP0_STAGE_SETUP: return "idle"; + case MUSB_EP0_STAGE_IDLE: return "idle"; + case MUSB_EP0_STAGE_SETUP: return "setup"; case MUSB_EP0_STAGE_TX: return "in"; case MUSB_EP0_STAGE_RX: return "out"; case MUSB_EP0_STAGE_ACKWAIT: return "wait"; @@ -628,7 +630,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) musb_writew(regs, MUSB_CSR0, csr & ~MUSB_CSR0_P_SENTSTALL); retval = IRQ_HANDLED; - musb->ep0_state = MUSB_EP0_STAGE_SETUP; + musb->ep0_state = MUSB_EP0_STAGE_IDLE; csr = musb_readw(regs, MUSB_CSR0); } @@ -636,7 +638,18 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) if (csr & MUSB_CSR0_P_SETUPEND) { musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SVDSETUPEND); retval = IRQ_HANDLED; - musb->ep0_state = MUSB_EP0_STAGE_SETUP; + /* Transition into the early status phase */ + switch (musb->ep0_state) { + case MUSB_EP0_STAGE_TX: + musb->ep0_state = MUSB_EP0_STAGE_STATUSOUT; + break; + case MUSB_EP0_STAGE_RX: + musb->ep0_state = MUSB_EP0_STAGE_STATUSIN; + break; + default: + ERR("SetupEnd came in a wrong ep0stage %s", + decode_ep0stage(musb->ep0_state)); + } csr = musb_readw(regs, MUSB_CSR0); /* NOTE: request may need completion */ } @@ -697,11 +710,31 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) if (req) musb_g_ep0_giveback(musb, req); } + + /* + * In case when several interrupts can get coalesced, + * check to see if we've already received a SETUP packet... + */ + if (csr & MUSB_CSR0_RXPKTRDY) + goto setup; + + retval = IRQ_HANDLED; + musb->ep0_state = MUSB_EP0_STAGE_IDLE; + break; + + case MUSB_EP0_STAGE_IDLE: + /* + * This state is typically (but not always) indiscernible + * from the status states since the corresponding interrupts + * tend to happen within too little period of time (with only + * a zero-length packet in between) and so get coalesced... + */ retval = IRQ_HANDLED; musb->ep0_state = MUSB_EP0_STAGE_SETUP; /* FALLTHROUGH */ case MUSB_EP0_STAGE_SETUP: +setup: if (csr & MUSB_CSR0_RXPKTRDY) { struct usb_ctrlrequest setup; int handled = 0; @@ -783,7 +816,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) stall: DBG(3, "stall (%d)\n", handled); musb->ackpend |= MUSB_CSR0_P_SENDSTALL; - musb->ep0_state = MUSB_EP0_STAGE_SETUP; + musb->ep0_state = MUSB_EP0_STAGE_IDLE; finish: musb_writew(regs, MUSB_CSR0, musb->ackpend); @@ -803,7 +836,7 @@ finish: /* "can't happen" */ WARN_ON(1); musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SENDSTALL); - musb->ep0_state = MUSB_EP0_STAGE_SETUP; + musb->ep0_state = MUSB_EP0_STAGE_IDLE; break; } @@ -959,7 +992,7 @@ static int musb_g_ep0_halt(struct usb_ep *e, int value) csr |= MUSB_CSR0_P_SENDSTALL; musb_writew(regs, MUSB_CSR0, csr); - musb->ep0_state = MUSB_EP0_STAGE_SETUP; + musb->ep0_state = MUSB_EP0_STAGE_IDLE; musb->ackpend = 0; break; default: -- cgit v1.2.3 From f2c529e4efac32787955b170a6662454c066847a Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 27 Mar 2009 12:53:32 -0700 Subject: musb_gadget: suppress "parasitic" TX interrupts with CPPI Suppress "parasitic" endpoint interrupts in the DMA mode when using CPPI DMA driver; they're caused by the MUSB gadget driver using the DMA request mode 0 instead of the mode 1. Signed-off-by: Sergei Shtylyov Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_gadget.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index f79440cdfe7e..bc197b28ac40 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -349,7 +349,8 @@ static void txstate(struct musb *musb, struct musb_request *req) #elif defined(CONFIG_USB_TI_CPPI_DMA) /* program endpoint CSR first, then setup DMA */ csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY); - csr |= MUSB_TXCSR_MODE | MUSB_TXCSR_DMAENAB; + csr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE | + MUSB_TXCSR_MODE; musb_writew(epio, MUSB_TXCSR, (MUSB_TXCSR_P_WZC_BITS & ~MUSB_TXCSR_P_UNDERRUN) | csr); -- cgit v1.2.3 From e7a0b76624b37b17b0100b6992f8968dcb9ba920 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 27 Mar 2009 12:54:21 -0700 Subject: musb_host: refactor musb_save_toggle() (take 2) Refactor musb_save_toggle() as follows: - replace 'struct musb_hw_ep *ep' parameter by 'struct musb_qh *qh' to avoid re-calculating this value - move usb_settogle() call out of the *if* operator. This is a net minor shrink of source and object code. Signed-off-by: Sergei Shtylyov Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_host.c | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index db1b57415ec7..bf194d244533 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -321,35 +321,24 @@ __acquires(musb->lock) spin_lock(&musb->lock); } -/* for bulk/interrupt endpoints only */ -static inline void -musb_save_toggle(struct musb_hw_ep *ep, int is_in, struct urb *urb) +/* For bulk/interrupt endpoints only */ +static inline void musb_save_toggle(struct musb_qh *qh, int is_in, + struct urb *urb) { - struct usb_device *udev = urb->dev; + void __iomem *epio = qh->hw_ep->regs; u16 csr; - void __iomem *epio = ep->regs; - struct musb_qh *qh; - /* FIXME: the current Mentor DMA code seems to have + /* + * FIXME: the current Mentor DMA code seems to have * problems getting toggle correct. */ - if (is_in || ep->is_shared_fifo) - qh = ep->in_qh; + if (is_in) + csr = musb_readw(epio, MUSB_RXCSR) & MUSB_RXCSR_H_DATATOGGLE; else - qh = ep->out_qh; + csr = musb_readw(epio, MUSB_TXCSR) & MUSB_TXCSR_H_DATATOGGLE; - if (!is_in) { - csr = musb_readw(epio, MUSB_TXCSR); - usb_settoggle(udev, qh->epnum, 1, - (csr & MUSB_TXCSR_H_DATATOGGLE) - ? 1 : 0); - } else { - csr = musb_readw(epio, MUSB_RXCSR); - usb_settoggle(udev, qh->epnum, 0, - (csr & MUSB_RXCSR_H_DATATOGGLE) - ? 1 : 0); - } + usb_settoggle(urb->dev, qh->epnum, !is_in, csr ? 1 : 0); } /* caller owns controller lock, irqs are blocked */ @@ -365,7 +354,7 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status) switch (qh->type) { case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: - musb_save_toggle(ep, is_in, urb); + musb_save_toggle(qh, is_in, urb); break; case USB_ENDPOINT_XFER_ISOC: if (status == 0 && urb->error_count) @@ -1427,7 +1416,7 @@ static void musb_bulk_rx_nak_timeout(struct musb *musb, struct musb_hw_ep *ep) urb->actual_length += dma->actual_len; dma->actual_len = 0L; } - musb_save_toggle(ep, 1, urb); + musb_save_toggle(cur_qh, 1, urb); /* move cur_qh to end of queue */ list_move_tail(&cur_qh->ring, &musb->in_bulk); -- cgit v1.2.3 From 4a80880e9cf2c06cecc193e0394713368772d5be Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 27 Mar 2009 12:55:16 -0700 Subject: musb_host: factor out musb_ep_{get|set}_qh() Factor out the often used code to get/set the active 'qh' pointer for the hardware endpoint. Change the way the case of a shared FIFO is handled by setting *both* 'in_qh' and 'out_qh' fields of 'struct musb_hw_ep'. That seems more consistent and makes getting to the current 'qh' easy when the code knows the direction beforehand. While at it, turn some assignments into intializers and fix declaration style in the vicinity. Signed-off-by: Sergei Shtylyov Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_host.c | 56 +++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index bf194d244533..17d14f26bd6c 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -181,6 +181,19 @@ static inline void musb_h_tx_dma_start(struct musb_hw_ep *ep) musb_writew(ep->regs, MUSB_TXCSR, txcsr); } +static void musb_ep_set_qh(struct musb_hw_ep *ep, int is_in, struct musb_qh *qh) +{ + if (is_in != 0 || ep->is_shared_fifo) + ep->in_qh = qh; + if (is_in == 0 || ep->is_shared_fifo) + ep->out_qh = qh; +} + +static struct musb_qh *musb_ep_get_qh(struct musb_hw_ep *ep, int is_in) +{ + return is_in ? ep->in_qh : ep->out_qh; +} + /* * Start the URB at the front of an endpoint's queue * end must be claimed from the caller. @@ -210,7 +223,6 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) case USB_ENDPOINT_XFER_CONTROL: /* control transfers always start with SETUP */ is_in = 0; - hw_ep->out_qh = qh; musb->ep0_stage = MUSB_EP0_START; buf = urb->setup_packet; len = 8; @@ -239,10 +251,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) epnum, buf + offset, len); /* Configure endpoint */ - if (is_in || hw_ep->is_shared_fifo) - hw_ep->in_qh = qh; - else - hw_ep->out_qh = qh; + musb_ep_set_qh(hw_ep, is_in, qh); musb_ep_program(musb, epnum, urb, !is_in, buf, offset, len); /* transmit may have more work: start it when it is time */ @@ -377,11 +386,8 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status) else ep->tx_reinit = 1; - /* clobber old pointers to this qh */ - if (is_in || ep->is_shared_fifo) - ep->in_qh = NULL; - else - ep->out_qh = NULL; + /* Clobber old pointers to this qh */ + musb_ep_set_qh(ep, is_in, NULL); qh->hep->hcpriv = NULL; switch (qh->type) { @@ -424,12 +430,7 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb, struct musb_hw_ep *hw_ep, int is_in) { - struct musb_qh *qh; - - if (is_in || hw_ep->is_shared_fifo) - qh = hw_ep->in_qh; - else - qh = hw_ep->out_qh; + struct musb_qh *qh = musb_ep_get_qh(hw_ep, is_in); if (urb->status == -EINPROGRESS) qh = musb_giveback(qh, urb, 0); @@ -692,15 +693,8 @@ static void musb_ep_program(struct musb *musb, u8 epnum, void __iomem *mbase = musb->mregs; struct musb_hw_ep *hw_ep = musb->endpoints + epnum; void __iomem *epio = hw_ep->regs; - struct musb_qh *qh; - u16 packet_sz; - - if (!is_out || hw_ep->is_shared_fifo) - qh = hw_ep->in_qh; - else - qh = hw_ep->out_qh; - - packet_sz = qh->maxpacket; + struct musb_qh *qh = musb_ep_get_qh(hw_ep, !is_out); + u16 packet_sz = qh->maxpacket; DBG(3, "%s hw%d urb %p spd%d dev%d ep%d%s " "h_addr%02x h_port%02x bytes %d\n", @@ -1118,17 +1112,14 @@ void musb_host_tx(struct musb *musb, u8 epnum) u16 tx_csr; size_t length = 0; size_t offset = 0; - struct urb *urb; struct musb_hw_ep *hw_ep = musb->endpoints + epnum; void __iomem *epio = hw_ep->regs; - struct musb_qh *qh = hw_ep->is_shared_fifo ? hw_ep->in_qh - : hw_ep->out_qh; + struct musb_qh *qh = hw_ep->out_qh; + struct urb *urb = next_urb(qh); u32 status = 0; void __iomem *mbase = musb->mregs; struct dma_channel *dma; - urb = next_urb(qh); - musb_ep_select(mbase, epnum); tx_csr = musb_readw(epio, MUSB_TXCSR); @@ -1806,10 +1797,7 @@ static int musb_schedule( epnum++, hw_ep++) { int diff; - if (is_in || hw_ep->is_shared_fifo) { - if (hw_ep->in_qh != NULL) - continue; - } else if (hw_ep->out_qh != NULL) + if (musb_ep_get_qh(hw_ep, is_in) != NULL) continue; if (hw_ep == musb->bulk_ep) -- cgit v1.2.3 From bcc63cc78e76f36167ebd810ed429ce4c76e0174 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 27 Mar 2009 12:56:26 -0700 Subject: musb_host: simplify check for active URB The existance of the scheduling list shouldn't matter in determining whether there's currectly an URB executing on a hardware endpoint. What should actually matter is the 'in_qh' or 'out_qh' fields of the 'struct musb_hw_ep' -- those are set in musb_start_urb() and cleared in musb_giveback() when the endpoint's URB list drains. Hence we should be able to replace the big *switch* statements in musb_urb_dequeue() and musb_h_disable() with mere musb_ep_get_qh() calls... While at it, do some more changes: - add 'is_in' variable to musb_urb_dequeue(); - remove the unnecessary 'epnum' variable from musb_h_disable(); - fix the comment style in the vicinity. This is a minor shrink of source and object code. Signed-off-by: Sergei Shtylyov Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_host.c | 72 +++++++++----------------------------------- 1 file changed, 14 insertions(+), 58 deletions(-) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 17d14f26bd6c..e0dacbb336d0 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2089,14 +2089,14 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { struct musb *musb = hcd_to_musb(hcd); struct musb_qh *qh; - struct list_head *sched; unsigned long flags; + int is_in = usb_pipein(urb->pipe); int ret; DBG(4, "urb=%p, dev%d ep%d%s\n", urb, usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe), - usb_pipein(urb->pipe) ? "in" : "out"); + is_in ? "in" : "out"); spin_lock_irqsave(&musb->lock, flags); ret = usb_hcd_check_unlink_urb(hcd, urb, status); @@ -2107,45 +2107,23 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) if (!qh) goto done; - /* Any URB not actively programmed into endpoint hardware can be + /* + * Any URB not actively programmed into endpoint hardware can be * immediately given back; that's any URB not at the head of an * endpoint queue, unless someday we get real DMA queues. And even * if it's at the head, it might not be known to the hardware... * - * Otherwise abort current transfer, pending dma, etc.; urb->status + * Otherwise abort current transfer, pending DMA, etc.; urb->status * has already been updated. This is a synchronous abort; it'd be * OK to hold off until after some IRQ, though. + * + * NOTE: qh is invalid unless !list_empty(&hep->urb_list) */ - if (!qh->is_ready || urb->urb_list.prev != &qh->hep->urb_list) - ret = -EINPROGRESS; - else { - switch (qh->type) { - case USB_ENDPOINT_XFER_CONTROL: - sched = &musb->control; - break; - case USB_ENDPOINT_XFER_BULK: - if (qh->mux == 1) { - if (usb_pipein(urb->pipe)) - sched = &musb->in_bulk; - else - sched = &musb->out_bulk; - break; - } - default: - /* REVISIT when we get a schedule tree, periodic - * transfers won't always be at the head of a - * singleton queue... - */ - sched = NULL; - break; - } - } - - /* NOTE: qh is invalid unless !list_empty(&hep->urb_list) */ - if (ret < 0 || (sched && qh != first_qh(sched))) { + if (!qh->is_ready + || urb->urb_list.prev != &qh->hep->urb_list + || musb_ep_get_qh(qh->hw_ep, is_in) != qh) { int ready = qh->is_ready; - ret = 0; qh->is_ready = 0; __musb_giveback(musb, urb, 0); qh->is_ready = ready; @@ -2169,13 +2147,11 @@ done: static void musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) { - u8 epnum = hep->desc.bEndpointAddress; + u8 is_in = hep->desc.bEndpointAddress & USB_DIR_IN; unsigned long flags; struct musb *musb = hcd_to_musb(hcd); - u8 is_in = epnum & USB_DIR_IN; struct musb_qh *qh; struct urb *urb; - struct list_head *sched; spin_lock_irqsave(&musb->lock, flags); @@ -2183,31 +2159,11 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) if (qh == NULL) goto exit; - switch (qh->type) { - case USB_ENDPOINT_XFER_CONTROL: - sched = &musb->control; - break; - case USB_ENDPOINT_XFER_BULK: - if (qh->mux == 1) { - if (is_in) - sched = &musb->in_bulk; - else - sched = &musb->out_bulk; - break; - } - default: - /* REVISIT when we get a schedule tree, periodic transfers - * won't always be at the head of a singleton queue... - */ - sched = NULL; - break; - } - - /* NOTE: qh is invalid unless !list_empty(&hep->urb_list) */ + /* NOTE: qh is invalid unless !list_empty(&hep->urb_list) */ - /* kick first urb off the hardware, if needed */ + /* Kick the first URB off the hardware, if needed */ qh->is_ready = 0; - if (!sched || qh == first_qh(sched)) { + if (musb_ep_get_qh(qh->hw_ep, is_in) == qh) { urb = next_urb(qh); /* make software (then hardware) stop ASAP */ -- cgit v1.2.3 From fc97345c34227cf3ac8223ec0f218e42a549d281 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 27 Mar 2009 12:57:50 -0700 Subject: musb_host: streamline musb_cleanup_urb() calls The argument for the 'is_in' parameter of musb_cleanup_urb() is always extracted from an URB that's passed to the function. So that parameter is superfluous; remove it. Signed-off-by: Sergei Shtylyov Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_host.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index e0dacbb336d0..e666f609ad7e 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2029,14 +2029,15 @@ done: * called with controller locked, irqs blocked * that hardware queue advances to the next transfer, unless prevented */ -static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh, int is_in) +static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh) { struct musb_hw_ep *ep = qh->hw_ep; void __iomem *epio = ep->regs; unsigned hw_end = ep->epnum; void __iomem *regs = ep->musb->mregs; - u16 csr; + int is_in = usb_pipein(urb->pipe); int status = 0; + u16 csr; musb_ep_select(regs, hw_end); @@ -2137,7 +2138,7 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) kfree(qh); } } else - ret = musb_cleanup_urb(urb, qh, urb->pipe & USB_DIR_IN); + ret = musb_cleanup_urb(urb, qh); done: spin_unlock_irqrestore(&musb->lock, flags); return ret; @@ -2171,7 +2172,7 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) urb->status = -ESHUTDOWN; /* cleanup */ - musb_cleanup_urb(urb, qh, urb->pipe & USB_DIR_IN); + musb_cleanup_urb(urb, qh); /* Then nuke all the others ... and advance the * queue on hw_ep (e.g. bulk ring) when we're done. -- cgit v1.2.3 From 89e5fdf9f5da0530a39affb444e9c900eff588f7 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 27 Mar 2009 12:58:31 -0700 Subject: musb_host: refactor URB giveback As musb_advance_schedule() is now the only remaning caller of musb_giveback() (and the only valid context of such call), just fold the latter into the former and then rename __musb_giveback() into musb_giveback(). This is a net minor shrink. Signed-off-by: Sergei Shtylyov Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_host.c | 54 ++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index e666f609ad7e..c1bb192ecbef 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -295,9 +295,8 @@ start: } } -/* caller owns controller lock, irqs are blocked */ -static void -__musb_giveback(struct musb *musb, struct urb *urb, int status) +/* Context: caller owns controller lock, IRQs are blocked */ +static void musb_giveback(struct musb *musb, struct urb *urb, int status) __releases(musb->lock) __acquires(musb->lock) { @@ -350,14 +349,22 @@ static inline void musb_save_toggle(struct musb_qh *qh, int is_in, usb_settoggle(urb->dev, qh->epnum, !is_in, csr ? 1 : 0); } -/* caller owns controller lock, irqs are blocked */ -static struct musb_qh * -musb_giveback(struct musb_qh *qh, struct urb *urb, int status) +/* + * Advance this hardware endpoint's queue, completing the specified URB and + * advancing to either the next URB queued to that qh, or else invalidating + * that qh and advancing to the next qh scheduled after the current one. + * + * Context: caller owns controller lock, IRQs are blocked + */ +static void musb_advance_schedule(struct musb *musb, struct urb *urb, + struct musb_hw_ep *hw_ep, int is_in) { + struct musb_qh *qh = musb_ep_get_qh(hw_ep, is_in); struct musb_hw_ep *ep = qh->hw_ep; - struct musb *musb = ep->musb; - int is_in = usb_pipein(urb->pipe); int ready = qh->is_ready; + int status; + + status = (urb->status == -EINPROGRESS) ? 0 : urb->status; /* save toggle eagerly, for paranoia */ switch (qh->type) { @@ -366,13 +373,13 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status) musb_save_toggle(qh, is_in, urb); break; case USB_ENDPOINT_XFER_ISOC: - if (status == 0 && urb->error_count) + if (urb->error_count) status = -EXDEV; break; } qh->is_ready = 0; - __musb_giveback(musb, urb, status); + musb_giveback(musb, urb, status); qh->is_ready = ready; /* reclaim resources (and bandwidth) ASAP; deschedule it, and @@ -416,31 +423,10 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status) break; } } - return qh; -} - -/* - * Advance this hardware endpoint's queue, completing the specified urb and - * advancing to either the next urb queued to that qh, or else invalidating - * that qh and advancing to the next qh scheduled after the current one. - * - * Context: caller owns controller lock, irqs are blocked - */ -static void -musb_advance_schedule(struct musb *musb, struct urb *urb, - struct musb_hw_ep *hw_ep, int is_in) -{ - struct musb_qh *qh = musb_ep_get_qh(hw_ep, is_in); - - if (urb->status == -EINPROGRESS) - qh = musb_giveback(qh, urb, 0); - else - qh = musb_giveback(qh, urb, urb->status); if (qh != NULL && qh->is_ready) { DBG(4, "... next ep%d %cX urb %p\n", - hw_ep->epnum, is_in ? 'R' : 'T', - next_urb(qh)); + hw_ep->epnum, is_in ? 'R' : 'T', next_urb(qh)); musb_start_urb(musb, is_in, qh); } } @@ -2126,7 +2112,7 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) int ready = qh->is_ready; qh->is_ready = 0; - __musb_giveback(musb, urb, 0); + musb_giveback(musb, urb, 0); qh->is_ready = ready; /* If nothing else (usually musb_giveback) is using it @@ -2188,7 +2174,7 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) * will activate any of these as it advances. */ while (!list_empty(&hep->urb_list)) - __musb_giveback(musb, next_urb(qh), -ESHUTDOWN); + musb_giveback(musb, next_urb(qh), -ESHUTDOWN); hep->hcpriv = NULL; list_del(&qh->ring); -- cgit v1.2.3 From e2c0de0fbb535d3e4a4d56bbf1c81e4c53239534 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 27 Mar 2009 12:59:46 -0700 Subject: musb: split out CPPI interrupt handler As DaVinci DM646x has a dedicated CPPI DMA interrupt, replace cppi_completion() (which has always been kind of layering violation) by a complete CPPI interrupt handler. [ dbrownell@users.sourceforge.net: only cppi_dma.c needs platform device header, not cppi_dma.h ] Signed-off-by: Dmitry Krivoschekov Signed-off-by: Sergei Shtylyov Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/cppi_dma.c | 34 +++++++++++++++++++++++++++++++--- drivers/usb/musb/cppi_dma.h | 6 ++++-- drivers/usb/musb/davinci.c | 14 ++++---------- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c index 1976e9b41800..c3577bbbae6c 100644 --- a/drivers/usb/musb/cppi_dma.c +++ b/drivers/usb/musb/cppi_dma.c @@ -6,6 +6,7 @@ * The TUSB6020, using VLYNQ, has CPPI that looks much like DaVinci. */ +#include #include #include "musb_core.h" @@ -1145,17 +1146,27 @@ static bool cppi_rx_scan(struct cppi *cppi, unsigned ch) return completed; } -void cppi_completion(struct musb *musb, u32 rx, u32 tx) +irqreturn_t cppi_interrupt(int irq, void *dev_id) { - void __iomem *tibase; - int i, index; + struct musb *musb = dev_id; struct cppi *cppi; + void __iomem *tibase; struct musb_hw_ep *hw_ep = NULL; + u32 rx, tx; + int i, index; cppi = container_of(musb->dma_controller, struct cppi, controller); tibase = musb->ctrl_base; + tx = musb_readl(tibase, DAVINCI_TXCPPI_MASKED_REG); + rx = musb_readl(tibase, DAVINCI_RXCPPI_MASKED_REG); + + if (!tx && !rx) + return IRQ_NONE; + + DBG(4, "CPPI IRQ Tx%x Rx%x\n", tx, rx); + /* process TX channels */ for (index = 0; tx; tx = tx >> 1, index++) { struct cppi_channel *tx_ch; @@ -1273,6 +1284,8 @@ void cppi_completion(struct musb *musb, u32 rx, u32 tx) /* write to CPPI EOI register to re-enable interrupts */ musb_writel(tibase, DAVINCI_CPPI_EOI_REG, 0); + + return IRQ_HANDLED; } /* Instantiate a software object representing a DMA controller. */ @@ -1280,6 +1293,9 @@ struct dma_controller *__init dma_controller_create(struct musb *musb, void __iomem *mregs) { struct cppi *controller; + struct device *dev = musb->controller; + struct platform_device *pdev = to_platform_device(dev); + int irq = platform_get_irq(pdev, 1); controller = kzalloc(sizeof *controller, GFP_KERNEL); if (!controller) @@ -1310,6 +1326,15 @@ dma_controller_create(struct musb *musb, void __iomem *mregs) return NULL; } + if (irq > 0) { + if (request_irq(irq, cppi_interrupt, 0, "cppi-dma", musb)) { + dev_err(dev, "request_irq %d failed!\n", irq); + dma_controller_destroy(&controller->controller); + return NULL; + } + controller->irq = irq; + } + return &controller->controller; } @@ -1322,6 +1347,9 @@ void dma_controller_destroy(struct dma_controller *c) cppi = container_of(c, struct cppi, controller); + if (cppi->irq) + free_irq(cppi->irq, cppi->musb); + /* assert: caller stopped the controller first */ dma_pool_destroy(cppi->pool); diff --git a/drivers/usb/musb/cppi_dma.h b/drivers/usb/musb/cppi_dma.h index 729b4071787b..8a39de3e6e47 100644 --- a/drivers/usb/musb/cppi_dma.h +++ b/drivers/usb/musb/cppi_dma.h @@ -119,6 +119,8 @@ struct cppi { void __iomem *mregs; /* Mentor regs */ void __iomem *tibase; /* TI/CPPI regs */ + int irq; + struct cppi_channel tx[4]; struct cppi_channel rx[4]; @@ -127,7 +129,7 @@ struct cppi { struct list_head tx_complete; }; -/* irq handling hook */ -extern void cppi_completion(struct musb *, u32 rx, u32 tx); +/* CPPI IRQ handler */ +extern irqreturn_t cppi_interrupt(int, void *); #endif /* end of ifndef _CPPI_DMA_H_ */ diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 898b52fcff5d..6e14e06ff820 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -265,6 +265,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci) irqreturn_t retval = IRQ_NONE; struct musb *musb = __hci; void __iomem *tibase = musb->ctrl_base; + struct cppi *cppi; u32 tmp; spin_lock_irqsave(&musb->lock, flags); @@ -281,16 +282,9 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci) /* CPPI interrupts share the same IRQ line, but have their own * mask, state, "vector", and EOI registers. */ - if (is_cppi_enabled()) { - u32 cppi_tx = musb_readl(tibase, DAVINCI_TXCPPI_MASKED_REG); - u32 cppi_rx = musb_readl(tibase, DAVINCI_RXCPPI_MASKED_REG); - - if (cppi_tx || cppi_rx) { - DBG(4, "CPPI IRQ t%x r%x\n", cppi_tx, cppi_rx); - cppi_completion(musb, cppi_rx, cppi_tx); - retval = IRQ_HANDLED; - } - } + cppi = container_of(musb->dma_controller, struct cppi, controller); + if (is_cppi_enabled() && musb->dma_controller && !cppi->irq) + retval = cppi_interrupt(irq, __hci); /* ack and handle non-CPPI interrupts */ tmp = musb_readl(tibase, DAVINCI_USB_INT_SRC_MASKED_REG); -- cgit v1.2.3 From 179bf68ac09a9833fe3aa6abeb08b8a6e54a9730 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sat, 28 Mar 2009 00:27:13 +0100 Subject: USB: move twl4030_usb's probe function to .devinit.text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A pointer to twl4030_usb_probe is passed to the core via platform_driver_register and so the function must not disappear when the .init sections are discarded. Otherwise (if also having HOTPLUG=y) unbinding and binding a device to the driver via sysfs will result in an oops as does a device being registered late. An alternative to this patch is using platform_driver_probe instead of platform_driver_register plus removing the pointer to the probe function from the struct platform_driver. Signed-off-by: Uwe Kleine-König Cc: Jouni Hogander Cc: Kalle Jokiniemi Cc: Andrew Morton Cc: David Brownell Cc: Tony Lindgren Cc: Kevin Hilman Cc: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/twl4030-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index d9478d0e1c8b..c34e63910acb 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -641,7 +641,7 @@ static int twl4030_set_host(struct otg_transceiver *x, struct usb_bus *host) return 0; } -static int __init twl4030_usb_probe(struct platform_device *pdev) +static int __devinit twl4030_usb_probe(struct platform_device *pdev) { struct twl4030_usb_data *pdata = pdev->dev.platform_data; struct twl4030_usb *twl; -- cgit v1.2.3 From b0aa61d37668357f54431318f85ee1cf83e38a02 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sat, 28 Mar 2009 00:27:00 +0100 Subject: USB: move r8a66597_hcd's probe function to .devinit.text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A pointer to r8a66597_probe is passed to the core via platform_driver_register and so the function must not disappear when the .init sections are discarded. Otherwise (if also having HOTPLUG=y) unbinding and binding a device to the driver via sysfs will result in an oops as does a device being registered late. An alternative to this patch is using platform_driver_probe instead of platform_driver_register plus removing the pointer to the probe function from the struct platform_driver. Signed-off-by: Uwe Kleine-König Acked-by: Yoshihiro Shimoda Cc: Magnus Damm Cc: Stephen Rothwell Cc: Paul Mundt Cc: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/r8a66597-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index f1626e58c141..3e1216ad86bd 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -2373,7 +2373,7 @@ static int __init_or_module r8a66597_remove(struct platform_device *pdev) return 0; } -static int __init r8a66597_probe(struct platform_device *pdev) +static int __devinit r8a66597_probe(struct platform_device *pdev) { #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK) char clk_name[8]; -- cgit v1.2.3 From 857a9c2bdb5f5cf216ede5490c55d0ffae1ef888 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sat, 28 Mar 2009 00:26:33 +0100 Subject: USB: move orion-ehci's probe function to .devinit.text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A pointer to ehci_orion_drv_probe is passed to the core via platform_driver_register and so the function must not disappear when the .init sections are discarded. Otherwise (if also having HOTPLUG=y) unbinding and binding a device to the driver via sysfs will result in an oops as does a device being registered late. An alternative to this patch is using platform_driver_probe instead of platform_driver_register plus removing the pointer to the probe function from the struct platform_driver. Signed-off-by: Uwe Kleine-König Cc: Ronen Shitrit Cc: Lennert Buytenhek Cc: Alan Stern Cc: David Brownell Cc: Nicolas Pitre Cc: Russell King Cc: Tzachi Perelstein Cc: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-orion.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 9d487908012e..17dc15407a07 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -187,7 +187,7 @@ ehci_orion_conf_mbus_windows(struct usb_hcd *hcd, } } -static int __init ehci_orion_drv_probe(struct platform_device *pdev) +static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) { struct orion_ehci_data *pd = pdev->dev.platform_data; struct resource *res; -- cgit v1.2.3 From 3ccb6198ab28d2865a4a74fa07ba5b4d48263708 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 31 Mar 2009 12:26:10 -0700 Subject: USB: twl4030-usb: fix minor reporting goofage Fix a reporting glitch in the twl4030 USB transceiver code. It wasn't properly distinguishing the two types of active USB link: ID grounded, vs not. In the current code that distinction doesn't much matter; in the future this bugfix should help support better USB controller communications. Provide a comment sorting out some of the cryptic bits of the manual: different sections use different names for key signals, and the register definitions don't help much without the explanations and diagrams. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/twl4030-usb.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index c34e63910acb..9e3e7a5c258b 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -217,6 +217,7 @@ /* In module TWL4030_MODULE_PM_MASTER */ #define PROTECT_KEY 0x0E +#define STS_HW_CONDITIONS 0x0F /* In module TWL4030_MODULE_PM_RECEIVER */ #define VUSB_DEDICATED1 0x7D @@ -351,15 +352,26 @@ static enum linkstat twl4030_usb_linkstat(struct twl4030_usb *twl) int status; int linkstat = USB_LINK_UNKNOWN; - /* STS_HW_CONDITIONS */ - status = twl4030_readb(twl, TWL4030_MODULE_PM_MASTER, 0x0f); + /* + * For ID/VBUS sensing, see manual section 15.4.8 ... + * except when using only battery backup power, two + * comparators produce VBUS_PRES and ID_PRES signals, + * which don't match docs elsewhere. But ... BIT(7) + * and BIT(2) of STS_HW_CONDITIONS, respectively, do + * seem to match up. If either is true the USB_PRES + * signal is active, the OTG module is activated, and + * its interrupt may be raised (may wake the system). + */ + status = twl4030_readb(twl, TWL4030_MODULE_PM_MASTER, + STS_HW_CONDITIONS); if (status < 0) dev_err(twl->dev, "USB link status err %d\n", status); - else if (status & BIT(7)) - linkstat = USB_LINK_VBUS; - else if (status & BIT(2)) - linkstat = USB_LINK_ID; - else + else if (status & (BIT(7) | BIT(2))) { + if (status & BIT(2)) + linkstat = USB_LINK_ID; + else + linkstat = USB_LINK_VBUS; + } else linkstat = USB_LINK_NONE; dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", -- cgit v1.2.3 From 9e8f0a4bf6fd7d3fa5fcfc83a746b0e8e4dec58d Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 31 Mar 2009 12:28:31 -0700 Subject: USB: nop-usb-xceiv: behave when linked as a module The NOP OTG transceiver driver needs to be usable from modules. Make sure its symbols are always accessible at both compile and link time, and make sure the device instance is allocated from the heap so that device lifetime rules are obeyed. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/nop-usb-xceiv.c | 25 ++++++++++--------------- include/linux/usb/otg.h | 4 ++-- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index c567168f89af..9ed5ea568679 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -22,8 +22,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Current status: - * this is to add "nop" transceiver for all those phy which is - * autonomous such as isp1504 etc. + * This provides a "nop" transceiver for PHYs which are + * autonomous such as isp1504, isp1707, etc. */ #include @@ -36,30 +36,25 @@ struct nop_usb_xceiv { struct device *dev; }; -static u64 nop_xceiv_dmamask = DMA_BIT_MASK(32); - -static struct platform_device nop_xceiv_device = { - .name = "nop_usb_xceiv", - .id = -1, - .dev = { - .dma_mask = &nop_xceiv_dmamask, - .coherent_dma_mask = DMA_BIT_MASK(32), - .platform_data = NULL, - }, -}; +static struct platform_device *pd; void usb_nop_xceiv_register(void) { - if (platform_device_register(&nop_xceiv_device) < 0) { + if (pd) + return; + pd = platform_device_register_simple("nop_usb_xceiv", -1, NULL, 0); + if (!pd) { printk(KERN_ERR "Unable to register usb nop transceiver\n"); return; } } +EXPORT_SYMBOL(usb_nop_xceiv_register); void usb_nop_xceiv_unregister(void) { - platform_device_unregister(&nop_xceiv_device); + platform_device_unregister(pd); } +EXPORT_SYMBOL(usb_nop_xceiv_unregister); static inline struct nop_usb_xceiv *xceiv_to_nop(struct otg_transceiver *x) { diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 1aaa826396a1..2443c0e7a80c 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -80,10 +80,10 @@ struct otg_transceiver { /* for board-specific init logic */ extern int otg_set_transceiver(struct otg_transceiver *); -#ifdef CONFIG_NOP_USB_XCEIV + +/* sometimes transceivers are accessed only through e.g. ULPI */ extern void usb_nop_xceiv_register(void); extern void usb_nop_xceiv_unregister(void); -#endif /* for usb host and peripheral controller drivers */ -- cgit v1.2.3 From 2819c5cb84f0b03bed8c4a03b5d3464188b004ec Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 31 Mar 2009 12:30:04 -0700 Subject: musb: proper hookup to transceiver drivers Let the otg_transceiver in MUSB be managed by an external driver; don't assume it's integrated. OMAP3 chips need it to be external, and there may be ways to interact with the transceiver which add functionality to the system. Platform init code is responsible for setting up the transeciver, probably using the NOP transceiver for integrated transceivers. External ones will use whatever the board init code provided, such as twl4030 or something more hands-off. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/Kconfig | 2 + drivers/usb/musb/blackfin.c | 11 +++-- drivers/usb/musb/davinci.c | 33 +++++++++----- drivers/usb/musb/musb_core.c | 96 ++++++++++++++++++++++------------------- drivers/usb/musb/musb_core.h | 2 +- drivers/usb/musb/musb_gadget.c | 38 ++++++++-------- drivers/usb/musb/musb_host.c | 2 +- drivers/usb/musb/musb_virthub.c | 20 ++++----- drivers/usb/musb/omap2430.c | 62 ++++++++++---------------- drivers/usb/musb/tusb6010.c | 70 +++++++++++++++++++----------- 10 files changed, 181 insertions(+), 155 deletions(-) diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index b66e8544d8b9..70073b157f0a 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -10,6 +10,7 @@ comment "Enable Host or Gadget support to see Inventra options" config USB_MUSB_HDRC depends on (USB || USB_GADGET) && HAVE_CLK depends on !SUPERH + select NOP_USB_XCEIV if ARCH_DAVINCI select TWL4030_USB if MACH_OMAP_3430SDP select USB_OTG_UTILS tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' @@ -55,6 +56,7 @@ comment "Blackfin high speed USB Support" config USB_TUSB6010 boolean "TUSB 6010 support" depends on USB_MUSB_HDRC && !USB_MUSB_SOC + select NOP_USB_XCEIV default y help The TUSB 6010 chip, from Texas Instruments, connects a discrete diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 786134852092..f2f66ebc7362 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -143,7 +143,7 @@ static void musb_conn_timer_handler(unsigned long _musb) u16 val; spin_lock_irqsave(&musb->lock, flags); - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_A_IDLE: case OTG_STATE_A_WAIT_BCON: /* Start a new session */ @@ -154,7 +154,7 @@ static void musb_conn_timer_handler(unsigned long _musb) val = musb_readw(musb->mregs, MUSB_DEVCTL); if (!(val & MUSB_DEVCTL_BDEVICE)) { gpio_set_value(musb->config->gpio_vrsel, 1); - musb->xceiv.state = OTG_STATE_A_WAIT_BCON; + musb->xceiv->state = OTG_STATE_A_WAIT_BCON; } else { gpio_set_value(musb->config->gpio_vrsel, 0); @@ -247,6 +247,11 @@ int __init musb_platform_init(struct musb *musb) } gpio_direction_output(musb->config->gpio_vrsel, 0); + usb_nop_xceiv_register(); + musb->xceiv = otg_get_transceiver(); + if (!musb->xceiv) + return -ENODEV; + if (ANOMALY_05000346) { bfin_write_USB_APHY_CALIB(ANOMALY_05000346_value); SSYNC(); @@ -291,7 +296,7 @@ int __init musb_platform_init(struct musb *musb) musb_conn_timer_handler, (unsigned long) musb); } if (is_peripheral_enabled(musb)) - musb->xceiv.set_power = bfin_set_power; + musb->xceiv->set_power = bfin_set_power; musb->isr = blackfin_interrupt; diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 6e14e06ff820..180d7daa4099 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -215,7 +215,7 @@ static void otg_timer(unsigned long _musb) DBG(7, "poll devctl %02x (%s)\n", devctl, otg_state_string(musb)); spin_lock_irqsave(&musb->lock, flags); - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_A_WAIT_VFALL: /* Wait till VBUS falls below SessionEnd (~0.2V); the 1.3 RTL * seems to mis-handle session "start" otherwise (or in our @@ -226,7 +226,7 @@ static void otg_timer(unsigned long _musb) mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); break; } - musb->xceiv.state = OTG_STATE_A_WAIT_VRISE; + musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG, MUSB_INTR_VBUSERROR << DAVINCI_USB_USBINT_SHIFT); break; @@ -251,7 +251,7 @@ static void otg_timer(unsigned long _musb) if (devctl & MUSB_DEVCTL_BDEVICE) mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); else - musb->xceiv.state = OTG_STATE_A_IDLE; + musb->xceiv->state = OTG_STATE_A_IDLE; break; default: break; @@ -325,21 +325,21 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci) * to stop registering in devctl. */ musb->int_usb &= ~MUSB_INTR_VBUSERROR; - musb->xceiv.state = OTG_STATE_A_WAIT_VFALL; + musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); WARNING("VBUS error workaround (delay coming)\n"); } else if (is_host_enabled(musb) && drvvbus) { musb->is_active = 1; MUSB_HST_MODE(musb); - musb->xceiv.default_a = 1; - musb->xceiv.state = OTG_STATE_A_WAIT_VRISE; + musb->xceiv->default_a = 1; + musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; portstate(musb->port1_status |= USB_PORT_STAT_POWER); del_timer(&otg_workaround); } else { musb->is_active = 0; MUSB_DEV_MODE(musb); - musb->xceiv.default_a = 0; - musb->xceiv.state = OTG_STATE_B_IDLE; + musb->xceiv->default_a = 0; + musb->xceiv->state = OTG_STATE_B_IDLE; portstate(musb->port1_status &= ~USB_PORT_STAT_POWER); } @@ -361,7 +361,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci) /* poll for ID change */ if (is_otg_enabled(musb) - && musb->xceiv.state == OTG_STATE_B_IDLE) + && musb->xceiv->state == OTG_STATE_B_IDLE) mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); spin_unlock_irqrestore(&musb->lock, flags); @@ -380,6 +380,11 @@ int __init musb_platform_init(struct musb *musb) void __iomem *tibase = musb->ctrl_base; u32 revision; + usb_nop_xceiv_register(); + musb->xceiv = otg_get_transceiver(); + if (!musb->xceiv) + return -ENODEV; + musb->mregs += DAVINCI_BASE_OFFSET; clk_enable(musb->clock); @@ -387,7 +392,7 @@ int __init musb_platform_init(struct musb *musb) /* returns zero if e.g. not clocked */ revision = musb_readl(tibase, DAVINCI_USB_VERSION_REG); if (revision == 0) - return -ENODEV; + goto fail; if (is_host_enabled(musb)) setup_timer(&otg_workaround, otg_timer, (unsigned long) musb); @@ -421,6 +426,10 @@ int __init musb_platform_init(struct musb *musb) musb->isr = davinci_interrupt; return 0; + +fail: + usb_nop_xceiv_unregister(); + return -ENODEV; } int musb_platform_exit(struct musb *musb) @@ -431,7 +440,7 @@ int musb_platform_exit(struct musb *musb) davinci_source_power(musb, 0 /*off*/, 1); /* delay, to avoid problems with module reload */ - if (is_host_enabled(musb) && musb->xceiv.default_a) { + if (is_host_enabled(musb) && musb->xceiv->default_a) { int maxdelay = 30; u8 devctl, warn = 0; @@ -460,5 +469,7 @@ int musb_platform_exit(struct musb *musb) clk_disable(musb->clock); + usb_nop_xceiv_unregister(); + return 0; } diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 324459b619f7..2460c3986c96 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -267,7 +267,7 @@ void musb_load_testpacket(struct musb *musb) const char *otg_state_string(struct musb *musb) { - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_A_IDLE: return "a_idle"; case OTG_STATE_A_WAIT_VRISE: return "a_wait_vrise"; case OTG_STATE_A_WAIT_BCON: return "a_wait_bcon"; @@ -302,11 +302,11 @@ void musb_otg_timer_func(unsigned long data) unsigned long flags; spin_lock_irqsave(&musb->lock, flags); - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_B_WAIT_ACON: DBG(1, "HNP: b_wait_acon timeout; back to b_peripheral\n"); musb_g_disconnect(musb); - musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb->is_active = 0; break; case OTG_STATE_A_WAIT_BCON: @@ -331,20 +331,20 @@ void musb_hnp_stop(struct musb *musb) void __iomem *mbase = musb->mregs; u8 reg; - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_A_PERIPHERAL: case OTG_STATE_A_WAIT_VFALL: case OTG_STATE_A_WAIT_BCON: DBG(1, "HNP: Switching back to A-host\n"); musb_g_disconnect(musb); - musb->xceiv.state = OTG_STATE_A_IDLE; + musb->xceiv->state = OTG_STATE_A_IDLE; MUSB_HST_MODE(musb); musb->is_active = 0; break; case OTG_STATE_B_HOST: DBG(1, "HNP: Disabling HR\n"); hcd->self.is_b_host = 0; - musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; MUSB_DEV_MODE(musb); reg = musb_readb(mbase, MUSB_POWER); reg |= MUSB_POWER_SUSPENDM; @@ -402,7 +402,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, if (devctl & MUSB_DEVCTL_HM) { #ifdef CONFIG_USB_MUSB_HDRC_HCD - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_A_SUSPEND: /* remote wakeup? later, GetPortStatus * will stop RESUME signaling @@ -425,12 +425,12 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, musb->rh_timer = jiffies + msecs_to_jiffies(20); - musb->xceiv.state = OTG_STATE_A_HOST; + musb->xceiv->state = OTG_STATE_A_HOST; musb->is_active = 1; usb_hcd_resume_root_hub(musb_to_hcd(musb)); break; case OTG_STATE_B_WAIT_ACON: - musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb->is_active = 1; MUSB_DEV_MODE(musb); break; @@ -441,11 +441,11 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, } #endif } else { - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { #ifdef CONFIG_USB_MUSB_HDRC_HCD case OTG_STATE_A_SUSPEND: /* possibly DISCONNECT is upcoming */ - musb->xceiv.state = OTG_STATE_A_HOST; + musb->xceiv->state = OTG_STATE_A_HOST; usb_hcd_resume_root_hub(musb_to_hcd(musb)); break; #endif @@ -490,7 +490,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, */ musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); musb->ep0_stage = MUSB_EP0_START; - musb->xceiv.state = OTG_STATE_A_IDLE; + musb->xceiv->state = OTG_STATE_A_IDLE; MUSB_HST_MODE(musb); musb_set_vbus(musb, 1); @@ -516,7 +516,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, * REVISIT: do delays from lots of DEBUG_KERNEL checks * make trouble here, keeping VBUS < 4.4V ? */ - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_A_HOST: /* recovery is dicey once we've gotten past the * initial stages of enumeration, but if VBUS @@ -602,11 +602,11 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, MUSB_HST_MODE(musb); /* indicate new connection to OTG machine */ - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_B_PERIPHERAL: if (int_usb & MUSB_INTR_SUSPEND) { DBG(1, "HNP: SUSPEND+CONNECT, now b_host\n"); - musb->xceiv.state = OTG_STATE_B_HOST; + musb->xceiv->state = OTG_STATE_B_HOST; hcd->self.is_b_host = 1; int_usb &= ~MUSB_INTR_SUSPEND; } else @@ -614,13 +614,13 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, break; case OTG_STATE_B_WAIT_ACON: DBG(1, "HNP: Waiting to switch to b_host state\n"); - musb->xceiv.state = OTG_STATE_B_HOST; + musb->xceiv->state = OTG_STATE_B_HOST; hcd->self.is_b_host = 1; break; default: if ((devctl & MUSB_DEVCTL_VBUS) == (3 << MUSB_DEVCTL_VBUS_SHIFT)) { - musb->xceiv.state = OTG_STATE_A_HOST; + musb->xceiv->state = OTG_STATE_A_HOST; hcd->self.is_b_host = 0; } break; @@ -650,7 +650,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, } } else if (is_peripheral_capable()) { DBG(1, "BUS RESET as %s\n", otg_state_string(musb)); - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { #ifdef CONFIG_USB_OTG case OTG_STATE_A_SUSPEND: /* We need to ignore disconnect on suspend @@ -673,12 +673,12 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, case OTG_STATE_B_WAIT_ACON: DBG(1, "HNP: RESET (%s), to b_peripheral\n", otg_state_string(musb)); - musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb_g_reset(musb); break; #endif case OTG_STATE_B_IDLE: - musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; /* FALLTHROUGH */ case OTG_STATE_B_PERIPHERAL: musb_g_reset(musb); @@ -763,7 +763,7 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, MUSB_MODE(musb), devctl); handled = IRQ_HANDLED; - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { #ifdef CONFIG_USB_MUSB_HDRC_HCD case OTG_STATE_A_HOST: case OTG_STATE_A_SUSPEND: @@ -805,7 +805,7 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, otg_state_string(musb), devctl, power); handled = IRQ_HANDLED; - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { #ifdef CONFIG_USB_MUSB_OTG case OTG_STATE_A_PERIPHERAL: /* @@ -817,10 +817,10 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, case OTG_STATE_B_PERIPHERAL: musb_g_suspend(musb); musb->is_active = is_otg_enabled(musb) - && musb->xceiv.gadget->b_hnp_enable; + && musb->xceiv->gadget->b_hnp_enable; if (musb->is_active) { #ifdef CONFIG_USB_MUSB_OTG - musb->xceiv.state = OTG_STATE_B_WAIT_ACON; + musb->xceiv->state = OTG_STATE_B_WAIT_ACON; DBG(1, "HNP: Setting timer for b_ase0_brst\n"); musb_otg_timer.data = (unsigned long)musb; mod_timer(&musb_otg_timer, jiffies @@ -834,9 +834,9 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, + msecs_to_jiffies(musb->a_wait_bcon)); break; case OTG_STATE_A_HOST: - musb->xceiv.state = OTG_STATE_A_SUSPEND; + musb->xceiv->state = OTG_STATE_A_SUSPEND; musb->is_active = is_otg_enabled(musb) - && musb->xceiv.host->b_hnp_enable; + && musb->xceiv->host->b_hnp_enable; break; case OTG_STATE_B_HOST: /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */ @@ -1682,7 +1682,7 @@ musb_vbus_store(struct device *dev, struct device_attribute *attr, spin_lock_irqsave(&musb->lock, flags); musb->a_wait_bcon = val; - if (musb->xceiv.state == OTG_STATE_A_WAIT_BCON) + if (musb->xceiv->state == OTG_STATE_A_WAIT_BCON) musb->is_active = 0; musb_platform_try_idle(musb, jiffies + msecs_to_jiffies(val)); spin_unlock_irqrestore(&musb->lock, flags); @@ -1743,8 +1743,8 @@ static void musb_irq_work(struct work_struct *data) struct musb *musb = container_of(data, struct musb, irq_work); static int old_state; - if (musb->xceiv.state != old_state) { - old_state = musb->xceiv.state; + if (musb->xceiv->state != old_state) { + old_state = musb->xceiv->state; sysfs_notify(&musb->controller->kobj, NULL, "mode"); } } @@ -1841,7 +1841,7 @@ static void musb_free(struct musb *musb) } #ifdef CONFIG_USB_MUSB_OTG - put_device(musb->xceiv.dev); + put_device(musb->xceiv->dev); #endif #ifdef CONFIG_USB_MUSB_HDRC_HCD @@ -1922,10 +1922,18 @@ bad_config: } } - /* assume vbus is off */ - - /* platform adjusts musb->mregs and musb->isr if needed, - * and activates clocks + /* The musb_platform_init() call: + * - adjusts musb->mregs and musb->isr if needed, + * - may initialize an integrated tranceiver + * - initializes musb->xceiv, usually by otg_get_transceiver() + * - activates clocks. + * - stops powering VBUS + * - assigns musb->board_set_vbus if host mode is enabled + * + * There are various transciever configurations. Blackfin, + * DaVinci, TUSB60x0, and others integrate them. OMAP3 uses + * external/discrete ones in various flavors (twl4030 family, + * isp1504, non-OTG, etc) mostly hooking up through ULPI. */ musb->isr = generic_interrupt; status = musb_platform_init(musb); @@ -1993,17 +2001,17 @@ bad_config: ? "DMA" : "PIO", musb->nIrq); -#ifdef CONFIG_USB_MUSB_HDRC_HCD - /* host side needs more setup, except for no-host modes */ - if (musb->board_mode != MUSB_PERIPHERAL) { + /* host side needs more setup */ + if (is_host_enabled(musb)) { struct usb_hcd *hcd = musb_to_hcd(musb); - if (musb->board_mode == MUSB_OTG) + otg_set_host(musb->xceiv, &hcd->self); + + if (is_otg_enabled(musb)) hcd->self.otg_port = 1; - musb->xceiv.host = &hcd->self; + musb->xceiv->host = &hcd->self; hcd->power_budget = 2 * (plat->power ? : 250); } -#endif /* CONFIG_USB_MUSB_HDRC_HCD */ /* For the host-only role, we can activate right away. * (We expect the ID pin to be forcibly grounded!!) @@ -2011,8 +2019,8 @@ bad_config: */ if (!is_otg_enabled(musb) && is_host_enabled(musb)) { MUSB_HST_MODE(musb); - musb->xceiv.default_a = 1; - musb->xceiv.state = OTG_STATE_A_IDLE; + musb->xceiv->default_a = 1; + musb->xceiv->state = OTG_STATE_A_IDLE; status = usb_add_hcd(musb_to_hcd(musb), -1, 0); if (status) @@ -2027,8 +2035,8 @@ bad_config: } else /* peripheral is enabled */ { MUSB_DEV_MODE(musb); - musb->xceiv.default_a = 0; - musb->xceiv.state = OTG_STATE_B_IDLE; + musb->xceiv->default_a = 0; + musb->xceiv->state = OTG_STATE_B_IDLE; status = musb_gadget_setup(musb); if (status) diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index c2a776ee1947..2b49c989f043 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -356,7 +356,7 @@ struct musb { u16 int_rx; u16 int_tx; - struct otg_transceiver xceiv; + struct otg_transceiver *xceiv; int nIrq; unsigned irq_wake:1; diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index bc197b28ac40..8dfad1189da2 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1406,7 +1406,7 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget) spin_lock_irqsave(&musb->lock, flags); - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_B_PERIPHERAL: /* NOTE: OTG state machine doesn't include B_SUSPENDED; * that's part of the standard usb 1.1 state machine, and @@ -1508,9 +1508,9 @@ static int musb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA) { struct musb *musb = gadget_to_musb(gadget); - if (!musb->xceiv.set_power) + if (!musb->xceiv->set_power) return -EOPNOTSUPP; - return otg_set_power(&musb->xceiv, mA); + return otg_set_power(musb->xceiv, mA); } static int musb_gadget_pullup(struct usb_gadget *gadget, int is_on) @@ -1733,11 +1733,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) spin_lock_irqsave(&musb->lock, flags); - /* REVISIT always use otg_set_peripheral(), handling - * issues including the root hub one below ... - */ - musb->xceiv.gadget = &musb->g; - musb->xceiv.state = OTG_STATE_B_IDLE; + otg_set_peripheral(musb->xceiv, &musb->g); musb->is_active = 1; /* FIXME this ignores the softconnect flag. Drivers are @@ -1749,6 +1745,8 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) if (!is_otg_enabled(musb)) musb_start(musb); + otg_set_peripheral(musb->xceiv, &musb->g); + spin_unlock_irqrestore(&musb->lock, flags); if (is_otg_enabled(musb)) { @@ -1762,8 +1760,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) if (retval < 0) { DBG(1, "add_hcd failed, %d\n", retval); spin_lock_irqsave(&musb->lock, flags); - musb->xceiv.gadget = NULL; - musb->xceiv.state = OTG_STATE_UNDEFINED; + otg_set_peripheral(musb->xceiv, NULL); musb->gadget_driver = NULL; musb->g.dev.driver = NULL; spin_unlock_irqrestore(&musb->lock, flags); @@ -1846,8 +1843,9 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) (void) musb_gadget_vbus_draw(&musb->g, 0); - musb->xceiv.state = OTG_STATE_UNDEFINED; + musb->xceiv->state = OTG_STATE_UNDEFINED; stop_activity(musb, driver); + otg_set_peripheral(musb->xceiv, NULL); DBG(3, "unregistering driver %s\n", driver->function); spin_unlock_irqrestore(&musb->lock, flags); @@ -1883,7 +1881,7 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver); void musb_g_resume(struct musb *musb) { musb->is_suspended = 0; - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_B_IDLE: break; case OTG_STATE_B_WAIT_ACON: @@ -1909,10 +1907,10 @@ void musb_g_suspend(struct musb *musb) devctl = musb_readb(musb->mregs, MUSB_DEVCTL); DBG(3, "devctl %02x\n", devctl); - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_B_IDLE: if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) - musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; break; case OTG_STATE_B_PERIPHERAL: musb->is_suspended = 1; @@ -1958,22 +1956,22 @@ void musb_g_disconnect(struct musb *musb) spin_lock(&musb->lock); } - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { default: #ifdef CONFIG_USB_MUSB_OTG DBG(2, "Unhandled disconnect %s, setting a_idle\n", otg_state_string(musb)); - musb->xceiv.state = OTG_STATE_A_IDLE; + musb->xceiv->state = OTG_STATE_A_IDLE; break; case OTG_STATE_A_PERIPHERAL: - musb->xceiv.state = OTG_STATE_A_WAIT_VFALL; + musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; break; case OTG_STATE_B_WAIT_ACON: case OTG_STATE_B_HOST: #endif case OTG_STATE_B_PERIPHERAL: case OTG_STATE_B_IDLE: - musb->xceiv.state = OTG_STATE_B_IDLE; + musb->xceiv->state = OTG_STATE_B_IDLE; break; case OTG_STATE_B_SRP_INIT: break; @@ -2029,10 +2027,10 @@ __acquires(musb->lock) * or else after HNP, as A-Device */ if (devctl & MUSB_DEVCTL_BDEVICE) { - musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb->g.is_a_peripheral = 0; } else if (is_otg_enabled(musb)) { - musb->xceiv.state = OTG_STATE_A_PERIPHERAL; + musb->xceiv->state = OTG_STATE_A_PERIPHERAL; musb->g.is_a_peripheral = 1; } else WARN_ON(1); diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index c1bb192ecbef..0d1f15336a9c 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2213,7 +2213,7 @@ static int musb_bus_suspend(struct usb_hcd *hcd) { struct musb *musb = hcd_to_musb(hcd); - if (musb->xceiv.state == OTG_STATE_A_SUSPEND) + if (musb->xceiv->state == OTG_STATE_A_SUSPEND) return 0; if (is_host_active(musb) && musb->is_active) { diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index bf677acc83db..d7e1bc44f00a 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -78,18 +78,18 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend) DBG(3, "Root port suspended, power %02x\n", power); musb->port1_status |= USB_PORT_STAT_SUSPEND; - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_A_HOST: - musb->xceiv.state = OTG_STATE_A_SUSPEND; + musb->xceiv->state = OTG_STATE_A_SUSPEND; musb->is_active = is_otg_enabled(musb) - && musb->xceiv.host->b_hnp_enable; + && musb->xceiv->host->b_hnp_enable; musb_platform_try_idle(musb, 0); break; #ifdef CONFIG_USB_MUSB_OTG case OTG_STATE_B_HOST: - musb->xceiv.state = OTG_STATE_B_WAIT_ACON; + musb->xceiv->state = OTG_STATE_B_WAIT_ACON; musb->is_active = is_otg_enabled(musb) - && musb->xceiv.host->b_hnp_enable; + && musb->xceiv->host->b_hnp_enable; musb_platform_try_idle(musb, 0); break; #endif @@ -116,7 +116,7 @@ static void musb_port_reset(struct musb *musb, bool do_reset) void __iomem *mbase = musb->mregs; #ifdef CONFIG_USB_MUSB_OTG - if (musb->xceiv.state == OTG_STATE_B_IDLE) { + if (musb->xceiv->state == OTG_STATE_B_IDLE) { DBG(2, "HNP: Returning from HNP; no hub reset from b_idle\n"); musb->port1_status &= ~USB_PORT_STAT_RESET; return; @@ -186,14 +186,14 @@ void musb_root_disconnect(struct musb *musb) usb_hcd_poll_rh_status(musb_to_hcd(musb)); musb->is_active = 0; - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_A_HOST: case OTG_STATE_A_SUSPEND: - musb->xceiv.state = OTG_STATE_A_WAIT_BCON; + musb->xceiv->state = OTG_STATE_A_WAIT_BCON; musb->is_active = 0; break; case OTG_STATE_A_WAIT_VFALL: - musb->xceiv.state = OTG_STATE_B_IDLE; + musb->xceiv->state = OTG_STATE_B_IDLE; break; default: DBG(1, "host disconnect (%s)\n", otg_state_string(musb)); @@ -332,7 +332,7 @@ int musb_hub_control( musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; usb_hcd_poll_rh_status(musb_to_hcd(musb)); /* NOTE: it might really be A_WAIT_BCON ... */ - musb->xceiv.state = OTG_STATE_A_HOST; + musb->xceiv->state = OTG_STATE_A_HOST; } put_unaligned(cpu_to_le32(musb->port1_status diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 60924ce08493..a2f443859358 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -61,17 +61,17 @@ static void musb_do_idle(unsigned long _musb) devctl = musb_readb(musb->mregs, MUSB_DEVCTL); - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_A_WAIT_BCON: devctl &= ~MUSB_DEVCTL_SESSION; musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); devctl = musb_readb(musb->mregs, MUSB_DEVCTL); if (devctl & MUSB_DEVCTL_BDEVICE) { - musb->xceiv.state = OTG_STATE_B_IDLE; + musb->xceiv->state = OTG_STATE_B_IDLE; MUSB_DEV_MODE(musb); } else { - musb->xceiv.state = OTG_STATE_A_IDLE; + musb->xceiv->state = OTG_STATE_A_IDLE; MUSB_HST_MODE(musb); } break; @@ -89,7 +89,7 @@ static void musb_do_idle(unsigned long _musb) musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; usb_hcd_poll_rh_status(musb_to_hcd(musb)); /* NOTE: it might really be A_WAIT_BCON ... */ - musb->xceiv.state = OTG_STATE_A_HOST; + musb->xceiv->state = OTG_STATE_A_HOST; } break; #endif @@ -97,9 +97,9 @@ static void musb_do_idle(unsigned long _musb) case OTG_STATE_A_HOST: devctl = musb_readb(musb->mregs, MUSB_DEVCTL); if (devctl & MUSB_DEVCTL_BDEVICE) - musb->xceiv.state = OTG_STATE_B_IDLE; + musb->xceiv->state = OTG_STATE_B_IDLE; else - musb->xceiv.state = OTG_STATE_A_WAIT_BCON; + musb->xceiv->state = OTG_STATE_A_WAIT_BCON; #endif default: break; @@ -118,7 +118,7 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout) /* Never idle if active, or when VBUS timeout is not set as host */ if (musb->is_active || ((musb->a_wait_bcon == 0) - && (musb->xceiv.state == OTG_STATE_A_WAIT_BCON))) { + && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) { DBG(4, "%s active, deleting timer\n", otg_state_string(musb)); del_timer(&musb_idle_timer); last_timer = jiffies; @@ -163,8 +163,8 @@ static void omap_set_vbus(struct musb *musb, int is_on) if (is_on) { musb->is_active = 1; - musb->xceiv.default_a = 1; - musb->xceiv.state = OTG_STATE_A_WAIT_VRISE; + musb->xceiv->default_a = 1; + musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; devctl |= MUSB_DEVCTL_SESSION; MUSB_HST_MODE(musb); @@ -175,8 +175,8 @@ static void omap_set_vbus(struct musb *musb, int is_on) * jumping right to B_IDLE... */ - musb->xceiv.default_a = 0; - musb->xceiv.state = OTG_STATE_B_IDLE; + musb->xceiv->default_a = 0; + musb->xceiv->state = OTG_STATE_B_IDLE; devctl &= ~MUSB_DEVCTL_SESSION; MUSB_DEV_MODE(musb); @@ -188,10 +188,6 @@ static void omap_set_vbus(struct musb *musb, int is_on) otg_state_string(musb), musb_readb(musb->mregs, MUSB_DEVCTL)); } -static int omap_set_power(struct otg_transceiver *x, unsigned mA) -{ - return 0; -} static int musb_platform_resume(struct musb *musb); @@ -202,24 +198,6 @@ int musb_platform_set_mode(struct musb *musb, u8 musb_mode) devctl |= MUSB_DEVCTL_SESSION; musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); - switch (musb_mode) { -#ifdef CONFIG_USB_MUSB_HDRC_HCD - case MUSB_HOST: - otg_set_host(&musb->xceiv, musb->xceiv.host); - break; -#endif -#ifdef CONFIG_USB_GADGET_MUSB_HDRC - case MUSB_PERIPHERAL: - otg_set_peripheral(&musb->xceiv, musb->xceiv.gadget); - break; -#endif -#ifdef CONFIG_USB_MUSB_OTG - case MUSB_OTG: - break; -#endif - default: - return -EINVAL; - } return 0; } @@ -231,6 +209,16 @@ int __init musb_platform_init(struct musb *musb) omap_cfg_reg(AE5_2430_USB0HS_STP); #endif + /* We require some kind of external transceiver, hooked + * up through ULPI. TWL4030-family PMICs include one, + * which needs a driver, drivers aren't always needed. + */ + musb->xceiv = otg_get_transceiver(); + if (!musb->xceiv) { + pr_err("HS USB OTG: no transceiver configured\n"); + return -ENODEV; + } + musb_platform_resume(musb); l = omap_readl(OTG_SYSCONFIG); @@ -257,8 +245,6 @@ int __init musb_platform_init(struct musb *musb) if (is_host_enabled(musb)) musb->board_set_vbus = omap_set_vbus; - if (is_peripheral_enabled(musb)) - musb->xceiv.set_power = omap_set_power; musb->a_wait_bcon = MUSB_TIMEOUT_A_WAIT_BCON; setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb); @@ -282,8 +268,7 @@ int musb_platform_suspend(struct musb *musb) l |= ENABLEWAKEUP; /* enable wakeup */ omap_writel(l, OTG_SYSCONFIG); - if (musb->xceiv.set_suspend) - musb->xceiv.set_suspend(&musb->xceiv, 1); + otg_set_suspend(musb->xceiv, 1); if (musb->set_clock) musb->set_clock(musb->clock, 0); @@ -300,8 +285,7 @@ static int musb_platform_resume(struct musb *musb) if (!musb->clock) return 0; - if (musb->xceiv.set_suspend) - musb->xceiv.set_suspend(&musb->xceiv, 0); + otg_set_suspend(musb->xceiv, 0); if (musb->set_clock) musb->set_clock(musb->clock, 1); diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 4ac1477d3569..88b587c703e9 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -259,6 +259,8 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf) tusb_fifo_read_unaligned(fifo, buf, len); } +static struct musb *the_musb; + #ifdef CONFIG_USB_GADGET_MUSB_HDRC /* This is used by gadget drivers, and OTG transceiver logic, allowing @@ -269,7 +271,7 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf) */ static int tusb_draw_power(struct otg_transceiver *x, unsigned mA) { - struct musb *musb = container_of(x, struct musb, xceiv); + struct musb *musb = the_musb; void __iomem *tbase = musb->ctrl_base; u32 reg; @@ -419,7 +421,7 @@ static void musb_do_idle(unsigned long _musb) spin_lock_irqsave(&musb->lock, flags); - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_A_WAIT_BCON: if ((musb->a_wait_bcon != 0) && (musb->idle_timeout == 0 @@ -483,7 +485,7 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout) /* Never idle if active, or when VBUS timeout is not set as host */ if (musb->is_active || ((musb->a_wait_bcon == 0) - && (musb->xceiv.state == OTG_STATE_A_WAIT_BCON))) { + && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) { DBG(4, "%s active, deleting timer\n", otg_state_string(musb)); del_timer(&musb_idle_timer); last_timer = jiffies; @@ -532,8 +534,8 @@ static void tusb_source_power(struct musb *musb, int is_on) if (musb->set_clock) musb->set_clock(musb->clock, 1); timer = OTG_TIMER_MS(OTG_TIME_A_WAIT_VRISE); - musb->xceiv.default_a = 1; - musb->xceiv.state = OTG_STATE_A_WAIT_VRISE; + musb->xceiv->default_a = 1; + musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; devctl |= MUSB_DEVCTL_SESSION; conf |= TUSB_DEV_CONF_USB_HOST_MODE; @@ -546,24 +548,24 @@ static void tusb_source_power(struct musb *musb, int is_on) /* If ID pin is grounded, we want to be a_idle */ otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); if (!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS)) { - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_A_WAIT_VRISE: case OTG_STATE_A_WAIT_BCON: - musb->xceiv.state = OTG_STATE_A_WAIT_VFALL; + musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; break; case OTG_STATE_A_WAIT_VFALL: - musb->xceiv.state = OTG_STATE_A_IDLE; + musb->xceiv->state = OTG_STATE_A_IDLE; break; default: - musb->xceiv.state = OTG_STATE_A_IDLE; + musb->xceiv->state = OTG_STATE_A_IDLE; } musb->is_active = 0; - musb->xceiv.default_a = 1; + musb->xceiv->default_a = 1; MUSB_HST_MODE(musb); } else { musb->is_active = 0; - musb->xceiv.default_a = 0; - musb->xceiv.state = OTG_STATE_B_IDLE; + musb->xceiv->default_a = 0; + musb->xceiv->state = OTG_STATE_B_IDLE; MUSB_DEV_MODE(musb); } @@ -674,7 +676,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) else default_a = is_host_enabled(musb); DBG(2, "Default-%c\n", default_a ? 'A' : 'B'); - musb->xceiv.default_a = default_a; + musb->xceiv->default_a = default_a; tusb_source_power(musb, default_a); /* Don't allow idling immediately */ @@ -686,7 +688,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) if (int_src & TUSB_INT_SRC_VBUS_SENSE_CHNG) { /* B-dev state machine: no vbus ~= disconnect */ - if ((is_otg_enabled(musb) && !musb->xceiv.default_a) + if ((is_otg_enabled(musb) && !musb->xceiv->default_a) || !is_host_enabled(musb)) { #ifdef CONFIG_USB_MUSB_HDRC_HCD /* ? musb_root_disconnect(musb); */ @@ -701,9 +703,9 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) if (otg_stat & TUSB_DEV_OTG_STAT_SESS_END) { DBG(1, "Forcing disconnect (no interrupt)\n"); - if (musb->xceiv.state != OTG_STATE_B_IDLE) { + if (musb->xceiv->state != OTG_STATE_B_IDLE) { /* INTR_DISCONNECT can hide... */ - musb->xceiv.state = OTG_STATE_B_IDLE; + musb->xceiv->state = OTG_STATE_B_IDLE; musb->int_usb |= MUSB_INTR_DISCONNECT; } musb->is_active = 0; @@ -717,7 +719,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) DBG(2, "vbus change, %s, otg %03x\n", otg_state_string(musb), otg_stat); - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_A_IDLE: DBG(2, "Got SRP, turning on VBUS\n"); musb_set_vbus(musb, 1); @@ -765,7 +767,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) DBG(4, "%s timer, %03x\n", otg_state_string(musb), otg_stat); - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_A_WAIT_VRISE: /* VBUS has probably been valid for a while now, * but may well have bounced out of range a bit @@ -777,7 +779,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) DBG(2, "devctl %02x\n", devctl); break; } - musb->xceiv.state = OTG_STATE_A_WAIT_BCON; + musb->xceiv->state = OTG_STATE_A_WAIT_BCON; musb->is_active = 0; idle_timeout = jiffies + msecs_to_jiffies(musb->a_wait_bcon); @@ -1093,9 +1095,14 @@ int __init musb_platform_init(struct musb *musb) { struct platform_device *pdev; struct resource *mem; - void __iomem *sync; + void __iomem *sync = NULL; int ret; + usb_nop_xceiv_register(); + musb->xceiv = otg_get_transceiver(); + if (!musb->xceiv) + return -ENODEV; + pdev = to_platform_device(musb->controller); /* dma address for async dma */ @@ -1106,14 +1113,16 @@ int __init musb_platform_init(struct musb *musb) mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!mem) { pr_debug("no sync dma resource?\n"); - return -ENODEV; + ret = -ENODEV; + goto done; } musb->sync = mem->start; sync = ioremap(mem->start, mem->end - mem->start + 1); if (!sync) { pr_debug("ioremap for sync failed\n"); - return -ENOMEM; + ret = -ENOMEM; + goto done; } musb->sync_va = sync; @@ -1126,28 +1135,37 @@ int __init musb_platform_init(struct musb *musb) if (ret) { printk(KERN_ERR "Could not start tusb6010 (%d)\n", ret); - return -ENODEV; + goto done; } musb->isr = tusb_interrupt; if (is_host_enabled(musb)) musb->board_set_vbus = tusb_source_power; - if (is_peripheral_enabled(musb)) - musb->xceiv.set_power = tusb_draw_power; + if (is_peripheral_enabled(musb)) { + musb->xceiv->set_power = tusb_draw_power; + the_musb = musb; + } setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb); +done: + if (ret < 0) { + if (sync) + iounmap(sync); + usb_nop_xceiv_unregister(); + } return ret; } int musb_platform_exit(struct musb *musb) { del_timer_sync(&musb_idle_timer); + the_musb = NULL; if (musb->board_set_power) musb->board_set_power(0); iounmap(musb->sync_va); - + usb_nop_xceiv_unregister(); return 0; } -- cgit v1.2.3 From 3d22a4ee4aa992a63732a85563078aca5f3947cd Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 31 Mar 2009 12:32:12 -0700 Subject: musb: otg timer cleanup Minor cleanup of OTG timer handling: * unify decls for OTG time constants, in the core header * set up and use that timer in a more normal way * move to the driver struct, so it's usable outside core And tighten use and setup of T(a_wait_bcon) so that if it's used, it's always valid. (If that timer expires, the A-device will stop powering VBUS. For non-OTG systems, that will be a surprise.) No behavioral changes, other than more consistency when applying that core HNP timeout. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 41 ++++++++++++++++++++++------------------- drivers/usb/musb/musb_core.h | 14 +++++++++++--- drivers/usb/musb/omap2430.c | 2 -- 3 files changed, 33 insertions(+), 24 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 2460c3986c96..8bd6bb1b04ef 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -112,6 +112,7 @@ #include "davinci.h" #endif +#define TA_WAIT_BCON(m) max_t(int, (m)->a_wait_bcon, OTG_TIME_A_WAIT_BCON) unsigned musb_debug; @@ -287,12 +288,6 @@ const char *otg_state_string(struct musb *musb) #ifdef CONFIG_USB_MUSB_OTG -/* - * See also USB_OTG_1-3.pdf 6.6.5 Timers - * REVISIT: Are the other timers done in the hardware? - */ -#define TB_ASE0_BRST 100 /* Min 3.125 ms */ - /* * Handles OTG hnp timeouts, such as b_ase0_brst */ @@ -320,10 +315,8 @@ void musb_otg_timer_func(unsigned long data) spin_unlock_irqrestore(&musb->lock, flags); } -static DEFINE_TIMER(musb_otg_timer, musb_otg_timer_func, 0, 0); - /* - * Stops the B-device HNP state. Caller must take care of locking. + * Stops the HNP transition. Caller must take care of locking. */ void musb_hnp_stop(struct musb *musb) { @@ -661,11 +654,12 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, musb_g_reset(musb); /* FALLTHROUGH */ case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */ - DBG(1, "HNP: Setting timer as %s\n", - otg_state_string(musb)); - musb_otg_timer.data = (unsigned long)musb; - mod_timer(&musb_otg_timer, jiffies - + msecs_to_jiffies(100)); + /* never use invalid T(a_wait_bcon) */ + DBG(1, "HNP: in %s, %d msec timeout\n", + otg_state_string(musb), + TA_WAIT_BCON(musb)); + mod_timer(&musb->otg_timer, jiffies + + msecs_to_jiffies(TA_WAIT_BCON(musb))); break; case OTG_STATE_A_PERIPHERAL: musb_hnp_stop(musb); @@ -822,9 +816,9 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, #ifdef CONFIG_USB_MUSB_OTG musb->xceiv->state = OTG_STATE_B_WAIT_ACON; DBG(1, "HNP: Setting timer for b_ase0_brst\n"); - musb_otg_timer.data = (unsigned long)musb; - mod_timer(&musb_otg_timer, jiffies - + msecs_to_jiffies(TB_ASE0_BRST)); + mod_timer(&musb->otg_timer, jiffies + + msecs_to_jiffies( + OTG_TIME_B_ASE0_BRST)); #endif } break; @@ -1681,7 +1675,8 @@ musb_vbus_store(struct device *dev, struct device_attribute *attr, } spin_lock_irqsave(&musb->lock, flags); - musb->a_wait_bcon = val; + /* force T(a_wait_bcon) to be zero/unlimited *OR* valid */ + musb->a_wait_bcon = val ? max_t(int, val, OTG_TIME_A_WAIT_BCON) : 0 ; if (musb->xceiv->state == OTG_STATE_A_WAIT_BCON) musb->is_active = 0; musb_platform_try_idle(musb, jiffies + msecs_to_jiffies(val)); @@ -1700,10 +1695,13 @@ musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf) spin_lock_irqsave(&musb->lock, flags); val = musb->a_wait_bcon; + /* FIXME get_vbus_status() is normally #defined as false... + * and is effectively TUSB-specific. + */ vbus = musb_platform_get_vbus_status(musb); spin_unlock_irqrestore(&musb->lock, flags); - return sprintf(buf, "Vbus %s, timeout %lu\n", + return sprintf(buf, "Vbus %s, timeout %lu msec\n", vbus ? "on" : "off", val); } static DEVICE_ATTR(vbus, 0644, musb_vbus_show, musb_vbus_store); @@ -1776,6 +1774,7 @@ allocate_instance(struct device *dev, hcd->uses_new_polling = 1; musb->vbuserr_retry = VBUSERR_RETRY_COUNT; + musb->a_wait_bcon = OTG_TIME_A_WAIT_BCON; #else musb = kzalloc(sizeof *musb, GFP_KERNEL); if (!musb) @@ -1970,6 +1969,10 @@ bad_config: if (status < 0) goto fail2; +#ifdef CONFIG_USB_OTG + setup_timer(&musb->otg_timer, musb_otg_timer_func, (unsigned long) musb); +#endif + /* Init IRQ workqueue before request_irq */ INIT_WORK(&musb->irq_work, musb_irq_work); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 2b49c989f043..78116fdb5781 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -180,10 +181,15 @@ enum musb_g_ep0_state { MUSB_EP0_STAGE_ACKWAIT, /* after zlp, before statusin */ } __attribute__ ((packed)); -/* OTG protocol constants */ +/* + * OTG protocol constants. See USB OTG 1.3 spec, + * sections 5.5 "Device Timings" and 6.6.5 "Timers". + */ #define OTG_TIME_A_WAIT_VRISE 100 /* msec (max) */ -#define OTG_TIME_A_WAIT_BCON 0 /* 0=infinite; min 1000 msec */ -#define OTG_TIME_A_IDLE_BDIS 200 /* msec (min) */ +#define OTG_TIME_A_WAIT_BCON 1100 /* min 1 second */ +#define OTG_TIME_A_AIDL_BDIS 200 /* min 200 msec */ +#define OTG_TIME_B_ASE0_BRST 100 /* min 3.125 ms */ + /*************************** REGISTER ACCESS ********************************/ @@ -332,6 +338,8 @@ struct musb { struct list_head control; /* of musb_qh */ struct list_head in_bulk; /* of musb_qh */ struct list_head out_bulk; /* of musb_qh */ + + struct timer_list otg_timer; #endif /* called with IRQs blocked; ON/nonzero implies starting a session, diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index a2f443859358..48930f25c450 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -44,7 +44,6 @@ #define get_cpu_rev() 2 #endif -#define MUSB_TIMEOUT_A_WAIT_BCON 1100 static struct timer_list musb_idle_timer; @@ -245,7 +244,6 @@ int __init musb_platform_init(struct musb *musb) if (is_host_enabled(musb)) musb->board_set_vbus = omap_set_vbus; - musb->a_wait_bcon = MUSB_TIMEOUT_A_WAIT_BCON; setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb); -- cgit v1.2.3 From dddaef0fe60d86e97d6609df0154f0ec193802a1 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 2 Apr 2009 10:16:11 -0700 Subject: musb: make initial HNP roleswitch work (v2) Minor HNP bugfixes, so the initial role switch works: - A-Device: * disconnect-during-suspend enters A_PERIPHERAL state * kill OTG timer after reset as A_PERIPHERAL ... * ... and also pass that reset to the gadget * once HNP succeeds, clear the "ignore_disconnect" flag * from A_PERIPHERAL, disconnect transitions to A_WAIT_BCON - B-Device: * kill OTG timer on entry to B_HOST state (HNP succeeded) * once HNP succeeds, clear "ignore_disconnect" flag * kick the root hub only _after_ the state is adjusted Other state transitions are left alone. Notably, exit paths from the "roles have switched" state ... A_PERIPHERAL handling of that stays seriously broken. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 27 ++++++++++++++++----------- drivers/usb/musb/musb_gadget.c | 2 +- drivers/usb/musb/musb_virthub.c | 11 ++++++++++- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 8bd6bb1b04ef..93dd23a7eb6c 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -587,28 +587,23 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, if (devctl & MUSB_DEVCTL_LSDEV) musb->port1_status |= USB_PORT_STAT_LOW_SPEED; - if (hcd->status_urb) - usb_hcd_poll_rh_status(hcd); - else - usb_hcd_resume_root_hub(hcd); - - MUSB_HST_MODE(musb); - /* indicate new connection to OTG machine */ switch (musb->xceiv->state) { case OTG_STATE_B_PERIPHERAL: if (int_usb & MUSB_INTR_SUSPEND) { DBG(1, "HNP: SUSPEND+CONNECT, now b_host\n"); - musb->xceiv->state = OTG_STATE_B_HOST; - hcd->self.is_b_host = 1; int_usb &= ~MUSB_INTR_SUSPEND; + goto b_host; } else DBG(1, "CONNECT as b_peripheral???\n"); break; case OTG_STATE_B_WAIT_ACON: - DBG(1, "HNP: Waiting to switch to b_host state\n"); + DBG(1, "HNP: CONNECT, now b_host\n"); +b_host: musb->xceiv->state = OTG_STATE_B_HOST; hcd->self.is_b_host = 1; + musb->ignore_disconnect = 0; + del_timer(&musb->otg_timer); break; default: if ((devctl & MUSB_DEVCTL_VBUS) @@ -618,6 +613,14 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, } break; } + + /* poke the root hub */ + MUSB_HST_MODE(musb); + if (hcd->status_urb) + usb_hcd_poll_rh_status(hcd); + else + usb_hcd_resume_root_hub(hcd); + DBG(1, "CONNECT (%s) devctl %02x\n", otg_state_string(musb), devctl); } @@ -662,7 +665,9 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, + msecs_to_jiffies(TA_WAIT_BCON(musb))); break; case OTG_STATE_A_PERIPHERAL: - musb_hnp_stop(musb); + musb->ignore_disconnect = 0; + del_timer(&musb->otg_timer); + musb_g_reset(musb); break; case OTG_STATE_B_WAIT_ACON: DBG(1, "HNP: RESET (%s), to b_peripheral\n", diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 8dfad1189da2..3c4da75fbc7b 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1964,7 +1964,7 @@ void musb_g_disconnect(struct musb *musb) musb->xceiv->state = OTG_STATE_A_IDLE; break; case OTG_STATE_A_PERIPHERAL: - musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; + musb->xceiv->state = OTG_STATE_A_WAIT_BCON; break; case OTG_STATE_B_WAIT_ACON: case OTG_STATE_B_HOST: diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index d7e1bc44f00a..c85a82a41d5c 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -187,8 +187,17 @@ void musb_root_disconnect(struct musb *musb) musb->is_active = 0; switch (musb->xceiv->state) { - case OTG_STATE_A_HOST: case OTG_STATE_A_SUSPEND: +#ifdef CONFIG_USB_MUSB_OTG + if (is_otg_enabled(musb) + && musb->xceiv->host->b_hnp_enable) { + musb->xceiv->state = OTG_STATE_A_PERIPHERAL; + musb->g.is_a_peripheral = 1; + break; + } +#endif + /* FALLTHROUGH */ + case OTG_STATE_A_HOST: musb->xceiv->state = OTG_STATE_A_WAIT_BCON; musb->is_active = 0; break; -- cgit v1.2.3 From 167a26421cee5d5ea7e8a4c6dac8c001f42c3189 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 31 Mar 2009 12:35:09 -0700 Subject: musb: support disconnect after HNP roleswitch Adjust HNP state machines in MUSB driver so that they handle the case where the cable is disconnected. The A-side machine was very wrong (unrecoverable); the B-Side was much less so. - A_PERIPHERAL ... as usual, the non-observability of the ID pin through Mentor's registers makes trouble. We can't go directly to A_WAIT_VFALL to end the session and start the disconnect processing. We can however sense link suspending, go to A_WAIT_BCON, and from there use OTG timeouts to finally trigger that A_WAIT_VFALL transition. (Hoping that nobody reconnects quickly to that port and notices the wrong state.) - B_HOST ... actually clear the Host Request (HR) bit as the messages say, disconnect the peripheral from the root hub, and don't detour through a suspend state. (In some cases this would eventually have cleaned up.) Also adjust the A_SUSPEND transition to respect the A_AIDL_BDIS timeout, so if HNP doesn't trigger quickly enough the A_WAIT_VFALL transition happens as it should. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 41 +++++++++++++++++++++++++++++------------ drivers/usb/musb/musb_gadget.c | 2 ++ drivers/usb/musb/musb_virthub.c | 4 ++++ 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 93dd23a7eb6c..b49859623f8d 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -304,9 +304,11 @@ void musb_otg_timer_func(unsigned long data) musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb->is_active = 0; break; + case OTG_STATE_A_SUSPEND: case OTG_STATE_A_WAIT_BCON: - DBG(1, "HNP: a_wait_bcon timeout; back to a_host\n"); - musb_hnp_stop(musb); + DBG(1, "HNP: %s timeout\n", otg_state_string(musb)); + musb_set_vbus(musb, 0); + musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; break; default: DBG(1, "HNP: Unhandled mode %s\n", otg_state_string(musb)); @@ -324,15 +326,12 @@ void musb_hnp_stop(struct musb *musb) void __iomem *mbase = musb->mregs; u8 reg; + DBG(1, "HNP: stop from %s\n", otg_state_string(musb)); + switch (musb->xceiv->state) { case OTG_STATE_A_PERIPHERAL: - case OTG_STATE_A_WAIT_VFALL: - case OTG_STATE_A_WAIT_BCON: - DBG(1, "HNP: Switching back to A-host\n"); musb_g_disconnect(musb); - musb->xceiv->state = OTG_STATE_A_IDLE; - MUSB_HST_MODE(musb); - musb->is_active = 0; + DBG(1, "HNP: back to %s\n", otg_state_string(musb)); break; case OTG_STATE_B_HOST: DBG(1, "HNP: Disabling HR\n"); @@ -775,7 +774,16 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, #endif /* HOST */ #ifdef CONFIG_USB_MUSB_OTG case OTG_STATE_B_HOST: - musb_hnp_stop(musb); + /* REVISIT this behaves for "real disconnect" + * cases; make sure the other transitions from + * from B_HOST act right too. The B_HOST code + * in hnp_stop() is currently not used... + */ + musb_root_disconnect(musb); + musb_to_hcd(musb)->self.is_b_host = 0; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; + MUSB_DEV_MODE(musb); + musb_g_disconnect(musb); break; case OTG_STATE_A_PERIPHERAL: musb_hnp_stop(musb); @@ -807,10 +815,19 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, switch (musb->xceiv->state) { #ifdef CONFIG_USB_MUSB_OTG case OTG_STATE_A_PERIPHERAL: - /* - * We cannot stop HNP here, devctl BDEVICE might be - * still set. + /* We also come here if the cable is removed, since + * this silicon doesn't report ID-no-longer-grounded. + * + * We depend on T(a_wait_bcon) to shut us down, and + * hope users don't do anything dicey during this + * undesired detour through A_WAIT_BCON. */ + musb_hnp_stop(musb); + usb_hcd_resume_root_hub(musb_to_hcd(musb)); + musb_root_disconnect(musb); + musb_platform_try_idle(musb, jiffies + + msecs_to_jiffies(musb->a_wait_bcon + ? : OTG_TIME_A_WAIT_BCON)); break; #endif case OTG_STATE_B_PERIPHERAL: diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 3c4da75fbc7b..858d005fae6d 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1962,9 +1962,11 @@ void musb_g_disconnect(struct musb *musb) DBG(2, "Unhandled disconnect %s, setting a_idle\n", otg_state_string(musb)); musb->xceiv->state = OTG_STATE_A_IDLE; + MUSB_HST_MODE(musb); break; case OTG_STATE_A_PERIPHERAL: musb->xceiv->state = OTG_STATE_A_WAIT_BCON; + MUSB_HST_MODE(musb); break; case OTG_STATE_B_WAIT_ACON: case OTG_STATE_B_HOST: diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index c85a82a41d5c..bfe5fe4ebfee 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -83,6 +83,10 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend) musb->xceiv->state = OTG_STATE_A_SUSPEND; musb->is_active = is_otg_enabled(musb) && musb->xceiv->host->b_hnp_enable; + if (musb->is_active) + mod_timer(&musb->otg_timer, jiffies + + msecs_to_jiffies( + OTG_TIME_A_AIDL_BDIS)); musb_platform_try_idle(musb, 0); break; #ifdef CONFIG_USB_MUSB_OTG -- cgit v1.2.3 From 7954d2856d2676484c34aff12440c0432ed8691c Mon Sep 17 00:00:00 2001 From: Anand Gadiyar Date: Thu, 2 Apr 2009 12:07:08 -0700 Subject: musb: use dma mode 1 for TX if transfer size equals maxpacket (v2) Currently, with Inventra DMA, we use Mode 0 if transfer size is less than or equal to the endpoint's maxpacket size. This requires that we explicitly set TXPKTRDY for that transfer. However the musb_g_tx code will not set TXPKTRDY twice if the last transfer is exactly equal to maxpacket, even if request->zero is set. Using Mode 1 will solve this; a better fix might be in musb_g_tx(). Without this change, musb will not correctly send out a ZLP if the last transfer is the maxpacket size and request->zero is set. Signed-off-by: Anand Gadiyar Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 858d005fae6d..8b3c4e2ed7b8 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -310,7 +310,7 @@ static void txstate(struct musb *musb, struct musb_request *req) /* setup DMA, then program endpoint CSR */ request_size = min(request->length, musb_ep->dma->max_len); - if (request_size <= musb_ep->packet_sz) + if (request_size < musb_ep->packet_sz) musb_ep->dma->desired_mode = 0; else musb_ep->dma->desired_mode = 1; -- cgit v1.2.3 From 5731845c81b46e94c7e12f66f21d2e5f72133824 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Gupta Date: Fri, 3 Apr 2009 16:16:17 -0700 Subject: musb: add high bandwidth ISO support Tested on OMAP3 host side with Creative (Live! Cam Optia) USB camera which uses high bandwidth isochronous IN endpoints. FIFO mode 4 is updated to provide the needed 4K endpoint buffer without breaking the g_nokia composite gadget configuration. (This is the only gadget driver known to use enough endpoints to notice the change.) Signed-off-by: Ajay Kumar Gupta Signed-off-by: David Brownell --- drivers/usb/musb/musb_core.c | 19 +++++++++--------- drivers/usb/musb/musb_core.h | 3 +++ drivers/usb/musb/musb_host.c | 47 +++++++++++++++++++++++++++++++++----------- drivers/usb/musb/musb_host.h | 1 + 4 files changed, 48 insertions(+), 22 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index b49859623f8d..554a414f65d1 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1084,14 +1084,13 @@ static struct fifo_cfg __initdata mode_4_cfg[] = { { .hw_ep_num = 8, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 9, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 9, .style = FIFO_RX, .maxpacket = 512, }, -{ .hw_ep_num = 10, .style = FIFO_TX, .maxpacket = 512, }, -{ .hw_ep_num = 10, .style = FIFO_RX, .maxpacket = 512, }, -{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 512, }, -{ .hw_ep_num = 11, .style = FIFO_RX, .maxpacket = 512, }, -{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 512, }, -{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 512, }, -{ .hw_ep_num = 13, .style = FIFO_TX, .maxpacket = 512, }, -{ .hw_ep_num = 13, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 10, .style = FIFO_TX, .maxpacket = 256, }, +{ .hw_ep_num = 10, .style = FIFO_RX, .maxpacket = 64, }, +{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 256, }, +{ .hw_ep_num = 11, .style = FIFO_RX, .maxpacket = 64, }, +{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 256, }, +{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 64, }, +{ .hw_ep_num = 13, .style = FIFO_RXTX, .maxpacket = 4096, }, { .hw_ep_num = 14, .style = FIFO_RXTX, .maxpacket = 1024, }, { .hw_ep_num = 15, .style = FIFO_RXTX, .maxpacket = 1024, }, }; @@ -1351,11 +1350,11 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) } if (reg & MUSB_CONFIGDATA_HBRXE) { strcat(aInfo, ", HB-ISO Rx"); - strcat(aInfo, " (X)"); /* no driver support */ + musb->hb_iso_rx = true; } if (reg & MUSB_CONFIGDATA_HBTXE) { strcat(aInfo, ", HB-ISO Tx"); - strcat(aInfo, " (X)"); /* no driver support */ + musb->hb_iso_tx = true; } if (reg & MUSB_CONFIGDATA_SOFTCONE) strcat(aInfo, ", SoftConn"); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 78116fdb5781..f3772ca3b2cf 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -395,6 +395,9 @@ struct musb { unsigned is_multipoint:1; unsigned ignore_disconnect:1; /* during bus resets */ + unsigned hb_iso_rx:1; /* high bandwidth iso rx? */ + unsigned hb_iso_tx:1; /* high bandwidth iso tx? */ + #ifdef C_MP_TX unsigned bulk_split:1; #define can_bulk_split(musb,type) \ diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 0d1f15336a9c..94a2a350a414 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -605,7 +605,8 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep) musb_writeb(ep->regs, MUSB_RXTYPE, qh->type_reg); musb_writeb(ep->regs, MUSB_RXINTERVAL, qh->intv_reg); /* NOTE: bulk combining rewrites high bits of maxpacket */ - musb_writew(ep->regs, MUSB_RXMAXP, qh->maxpacket); + musb_writew(ep->regs, MUSB_RXMAXP, + qh->maxpacket | ((qh->hb_mult - 1) << 11)); ep->rx_reinit = 0; } @@ -627,9 +628,10 @@ static bool musb_tx_dma_program(struct dma_controller *dma, csr = musb_readw(epio, MUSB_TXCSR); if (length > pkt_size) { mode = 1; - csr |= MUSB_TXCSR_AUTOSET - | MUSB_TXCSR_DMAMODE - | MUSB_TXCSR_DMAENAB; + csr |= MUSB_TXCSR_DMAMODE | MUSB_TXCSR_DMAENAB; + /* autoset shouldn't be set in high bandwidth */ + if (qh->hb_mult == 1) + csr |= MUSB_TXCSR_AUTOSET; } else { mode = 0; csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE); @@ -1497,6 +1499,10 @@ void musb_host_rx(struct musb *musb, u8 epnum) /* packet error reported later */ iso_err = true; } + } else if (rx_csr & MUSB_RXCSR_INCOMPRX) { + DBG(3, "end %d high bandwidth incomplete ISO packet RX\n", + epnum); + status = -EPROTO; } /* faults abort the transfer */ @@ -1704,7 +1710,11 @@ void musb_host_rx(struct musb *musb, u8 epnum) val &= ~MUSB_RXCSR_H_AUTOREQ; else val |= MUSB_RXCSR_H_AUTOREQ; - val |= MUSB_RXCSR_AUTOCLEAR | MUSB_RXCSR_DMAENAB; + val |= MUSB_RXCSR_DMAENAB; + + /* autoclear shouldn't be set in high bandwidth */ + if (qh->hb_mult == 1) + val |= MUSB_RXCSR_AUTOCLEAR; musb_writew(epio, MUSB_RXCSR, MUSB_RXCSR_H_WZC_BITS | val); @@ -1790,9 +1800,10 @@ static int musb_schedule( continue; if (is_in) - diff = hw_ep->max_packet_sz_rx - qh->maxpacket; + diff = hw_ep->max_packet_sz_rx; else - diff = hw_ep->max_packet_sz_tx - qh->maxpacket; + diff = hw_ep->max_packet_sz_tx; + diff -= (qh->maxpacket * qh->hb_mult); if (diff >= 0 && best_diff > diff) { best_diff = diff; @@ -1895,15 +1906,27 @@ static int musb_urb_enqueue( qh->is_ready = 1; qh->maxpacket = le16_to_cpu(epd->wMaxPacketSize); + qh->type = usb_endpoint_type(epd); - /* no high bandwidth support yet */ - if (qh->maxpacket & ~0x7ff) { - ret = -EMSGSIZE; - goto done; + /* Bits 11 & 12 of wMaxPacketSize encode high bandwidth multiplier. + * Some musb cores don't support high bandwidth ISO transfers; and + * we don't (yet!) support high bandwidth interrupt transfers. + */ + qh->hb_mult = 1 + ((qh->maxpacket >> 11) & 0x03); + if (qh->hb_mult > 1) { + int ok = (qh->type == USB_ENDPOINT_XFER_ISOC); + + if (ok) + ok = (usb_pipein(urb->pipe) && musb->hb_iso_rx) + || (usb_pipeout(urb->pipe) && musb->hb_iso_tx); + if (!ok) { + ret = -EMSGSIZE; + goto done; + } + qh->maxpacket &= 0x7ff; } qh->epnum = usb_endpoint_num(epd); - qh->type = usb_endpoint_type(epd); /* NOTE: urb->dev->devnum is wrong during SET_ADDRESS */ qh->addr_reg = (u8) usb_pipedevice(urb->pipe); diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index 0b7fbcd21963..14b00776638d 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -67,6 +67,7 @@ struct musb_qh { u8 is_ready; /* safe to modify hw_ep */ u8 type; /* XFERTYPE_* */ u8 epnum; + u8 hb_mult; /* high bandwidth pkts per uf */ u16 maxpacket; u16 frame; /* for periodic schedule */ unsigned iso_idx; /* in urb->iso_frame_desc[] */ -- cgit v1.2.3 From 8da464889cc87487a094ad25fd56edd6ecc61342 Mon Sep 17 00:00:00 2001 From: Mark Adamson Date: Thu, 9 Apr 2009 15:03:09 +0100 Subject: USB: serial: FTDI: add high speed device support Added support for FTDI's USB 2.0 hi-speed devices - FT2232H (2 interfaces) and FT4232H (4 interfaces), including a new baud rate calculation for these devices which can now achieve up to 12Mbaud by turning off a divide by 2.5 in the baud rate generator of the chips. In order to achieve baud rates of <1200 baud, the divide by 2.5 must be active. The default product ID of the FT2232H is 0x6010 (same as the FT2232C IC). The default PID of the FT4232H is 0x6011. Signed-off-by: Mark J. Adamson Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 90 ++++++++++++++++++++++++++++++++++++++----- drivers/usb/serial/ftdi_sio.h | 10 ++++- 2 files changed, 89 insertions(+), 11 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 8100f1d25904..80262e368cd5 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -47,7 +47,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.4.3" +#define DRIVER_VERSION "v1.5.0" #define DRIVER_AUTHOR "Greg Kroah-Hartman , Bill Ryder , Kuba Ober " #define DRIVER_DESC "USB FTDI Serial Converters Driver" @@ -81,7 +81,8 @@ struct ftdi_private { int rx_processed; unsigned long rx_bytes; - __u16 interface; /* FT2232C port interface (0 for FT232/245) */ + __u16 interface; /* FT2232C, FT2232H or FT4232H port interface + (0 for FT232/245) */ speed_t force_baud; /* if non-zero, force the baud rate to this value */ @@ -162,6 +163,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) }, { USB_DEVICE(FTDI_VID, FTDI_232RL_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_4232H_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MICRO_CHAMELEON_PID) }, { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) }, { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_PID) }, @@ -689,6 +691,8 @@ static const char *ftdi_chip_name[] = { [FT232BM] = "FT232BM", [FT2232C] = "FT2232C", [FT232RL] = "FT232RL", + [FT2232H] = "FT2232H", + [FT4232H] = "FT4232H" }; @@ -740,6 +744,8 @@ static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base); static unsigned short int ftdi_232am_baud_to_divisor(int baud); static __u32 ftdi_232bm_baud_base_to_divisor(int baud, int base); static __u32 ftdi_232bm_baud_to_divisor(int baud); +static __u32 ftdi_2232h_baud_base_to_divisor(int baud, int base); +static __u32 ftdi_2232h_baud_to_divisor(int baud); static struct usb_serial_driver ftdi_sio_device = { .driver = { @@ -833,6 +839,36 @@ static __u32 ftdi_232bm_baud_to_divisor(int baud) return ftdi_232bm_baud_base_to_divisor(baud, 48000000); } +static __u32 ftdi_2232h_baud_base_to_divisor(int baud, int base) +{ + static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 }; + __u32 divisor; + int divisor3; + + /* hi-speed baud rate is 10-bit sampling instead of 16-bit */ + divisor3 = (base / 10 / baud) * 8; + + divisor = divisor3 >> 3; + divisor |= (__u32)divfrac[divisor3 & 0x7] << 14; + /* Deal with special cases for highest baud rates. */ + if (divisor == 1) + divisor = 0; + else if (divisor == 0x4001) + divisor = 1; + /* + * Set this bit to turn off a divide by 2.5 on baud rate generator + * This enables baud rates up to 12Mbaud but cannot reach below 1200 + * baud with this bit set + */ + divisor |= 0x00020000; + return divisor; +} + +static __u32 ftdi_2232h_baud_to_divisor(int baud) +{ + return ftdi_2232h_baud_base_to_divisor(baud, 120000000); +} + #define set_mctrl(port, set) update_mctrl((port), (set), 0) #define clear_mctrl(port, clear) update_mctrl((port), 0, (clear)) @@ -991,6 +1027,19 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty, baud = 9600; } break; + case FT2232H: /* FT2232H chip */ + case FT4232H: /* FT4232H chip */ + if ((baud <= 12000000) & (baud >= 1200)) { + div_value = ftdi_2232h_baud_to_divisor(baud); + } else if (baud < 1200) { + div_value = ftdi_232bm_baud_to_divisor(baud); + } else { + dbg("%s - Baud rate too high!", __func__); + div_value = ftdi_232bm_baud_to_divisor(9600); + div_okay = 0; + baud = 9600; + } + break; } /* priv->chip_type */ if (div_okay) { @@ -1143,14 +1192,29 @@ static void ftdi_determine_type(struct usb_serial_port *port) if (interfaces > 1) { int inter; - /* Multiple interfaces. Assume FT2232C. */ - priv->chip_type = FT2232C; + /* Multiple interfaces.*/ + if (version == 0x0800) { + priv->chip_type = FT4232H; + /* Hi-speed - baud clock runs at 120MHz */ + priv->baud_base = 120000000 / 2; + } else if (version == 0x0700) { + priv->chip_type = FT2232H; + /* Hi-speed - baud clock runs at 120MHz */ + priv->baud_base = 120000000 / 2; + } else + priv->chip_type = FT2232C; + /* Determine interface code. */ inter = serial->interface->altsetting->desc.bInterfaceNumber; - if (inter == 0) - priv->interface = PIT_SIOA; - else - priv->interface = PIT_SIOB; + if (inter == 0) { + priv->interface = INTERFACE_A; + } else if (inter == 1) { + priv->interface = INTERFACE_B; + } else if (inter == 2) { + priv->interface = INTERFACE_C; + } else if (inter == 3) { + priv->interface = INTERFACE_D; + } /* BM-type devices have a bug where bcdDevice gets set * to 0x200 when iSerialNumber is 0. */ if (version < 0x500) { @@ -1287,7 +1351,9 @@ static int create_sysfs_attrs(struct usb_serial_port *port) if ((!retval) && (priv->chip_type == FT232BM || priv->chip_type == FT2232C || - priv->chip_type == FT232RL)) { + priv->chip_type == FT232RL || + priv->chip_type == FT2232H || + priv->chip_type == FT4232H)) { retval = device_create_file(&port->dev, &dev_attr_latency_timer); } @@ -1306,7 +1372,9 @@ static void remove_sysfs_attrs(struct usb_serial_port *port) device_remove_file(&port->dev, &dev_attr_event_char); if (priv->chip_type == FT232BM || priv->chip_type == FT2232C || - priv->chip_type == FT232RL) { + priv->chip_type == FT232RL || + priv->chip_type == FT2232H || + priv->chip_type == FT4232H) { device_remove_file(&port->dev, &dev_attr_latency_timer); } } @@ -2300,6 +2368,8 @@ static int ftdi_tiocmget(struct tty_struct *tty, struct file *file) case FT232BM: case FT2232C: case FT232RL: + case FT2232H: + case FT4232H: /* the 8U232AM returns a two byte value (the sio is a 1 byte value) - in the same format as the data returned from the in point */ diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index c09f658a448b..c21b2676c37a 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -10,7 +10,7 @@ * The device is based on the FTDI FT8U100AX chip. It has a DB25 on one side, * USB on the other. * - * Thanx to FTDI (http://www.ftdi.co.uk) for so kindly providing details + * Thanx to FTDI (http://www.ftdichip.com) for so kindly providing details * of the protocol required to talk to the device and ongoing assistence * during development. * @@ -28,6 +28,7 @@ #define FTDI_8U232AM_ALT_PID 0x6006 /* FTDI's alternate PID for above */ #define FTDI_8U2232C_PID 0x6010 /* Dual channel device */ #define FTDI_232RL_PID 0xFBFA /* Product ID for FT232RL */ +#define FTDI_4232H_PID 0x6011 /* Quad channel hi-speed device */ #define FTDI_RELAIS_PID 0xFA10 /* Relais device from Rudolf Gugler */ #define FTDI_NF_RIC_VID 0x0DCD /* Vendor Id */ #define FTDI_NF_RIC_PID 0x0001 /* Product Id */ @@ -873,6 +874,11 @@ #define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */ #define FTDI_SIO_GET_LATENCY_TIMER 10 /* Get the latency timer */ +/* Interface indicies for FT2232, FT2232H and FT4232H devices*/ +#define INTERFACE_A 1 +#define INTERFACE_B 2 +#define INTERFACE_C 3 +#define INTERFACE_D 4 /* * FIC / OpenMoko, Inc. http://wiki.openmoko.org/wiki/Neo1973_Debug_Board_v3 @@ -1030,6 +1036,8 @@ typedef enum { FT232BM = 3, FT2232C = 4, FT232RL = 5, + FT2232H = 6, + FT4232H = 7 } ftdi_chip_type_t; typedef enum { -- cgit v1.2.3 From 80f2fd2c763b27b53a37e2f7fd859cfbedd81a66 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 16 Apr 2009 15:35:09 -0400 Subject: USB: consolidate usb_unbind_interface and usb_driver_release_interface This patch (as1230) consolidates code in usb_unbind_interface() and usb_driver_release_interface(). In fact, it makes release_interface call unbind_interface, thereby removing the need for duplicated code. It works like this: If the interface has already been registered with the driver core when a driver releases it, then the usual driver-core mechanism will call unbind_interface. If it hasn't been unregistered then we will make the call ourselves. As a nice bonus, drivers now don't have to worry about whether their disconnect method will get called when they release an interface -- it always will. Previously it would be called only if the interface was registered. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index d0a21a5f8201..c115eed0fdc3 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -385,7 +385,6 @@ void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface) { struct device *dev = &iface->dev; - struct usb_device *udev = interface_to_usbdev(iface); /* this should never happen, don't release something that's not ours */ if (!dev->driver || dev->driver != &driver->drvwrap.driver) @@ -394,23 +393,19 @@ void usb_driver_release_interface(struct usb_driver *driver, /* don't release from within disconnect() */ if (iface->condition != USB_INTERFACE_BOUND) return; + iface->condition = USB_INTERFACE_UNBINDING; - /* don't release if the interface hasn't been added yet */ + /* Release via the driver core only if the interface + * has already been registered + */ if (device_is_registered(dev)) { - iface->condition = USB_INTERFACE_UNBINDING; device_release_driver(dev); } else { - iface->condition = USB_INTERFACE_UNBOUND; - usb_cancel_queued_reset(iface); + down(&dev->sem); + usb_unbind_interface(dev); + dev->driver = NULL; + up(&dev->sem); } - dev->driver = NULL; - usb_set_intfdata(iface, NULL); - - usb_pm_lock(udev); - iface->condition = USB_INTERFACE_UNBOUND; - mark_quiesced(iface); - iface->needs_remote_wakeup = 0; - usb_pm_unlock(udev); } EXPORT_SYMBOL_GPL(usb_driver_release_interface); -- cgit v1.2.3 From 3d41c949748182140465b04363d698539b6c7f99 Mon Sep 17 00:00:00 2001 From: Scott James Remnant Date: Tue, 21 Apr 2009 17:21:40 +0100 Subject: USB: usbfs: deprecate and hide option for !embedded Modern systems do not use usbfs; the entries within it are files, not device nodes, and do not support ACLs which are the default way to provide access to USB devices to untrusted users. It is replaced by device-nodes maintained by udev in /dev/bus/usb, libusb uses this device nodes. Mark the option as deprecated, and hide entirely for non-embedded builds (which may not be using udev but require raw USB device access). Signed-off-by: Scott James Remnant Acked-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/Kconfig | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index e1759d17ac5d..69280c35b5cb 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -28,7 +28,7 @@ comment "Miscellaneous USB options" depends on USB config USB_DEVICEFS - bool "USB device filesystem" + bool "USB device filesystem (DEPRECATED)" if EMBEDDED depends on USB ---help--- If you say Y here (and to "/proc file system support" in the "File @@ -46,11 +46,15 @@ config USB_DEVICEFS For the format of the various /proc/bus/usb/ files, please read . - Usbfs files can't handle Access Control Lists (ACL), which are the - default way to grant access to USB devices for untrusted users of a - desktop system. The usbfs functionality is replaced by real - device-nodes managed by udev. These nodes live in /dev/bus/usb and - are used by libusb. + Modern Linux systems do not use this. + + Usbfs entries are files and not character devices; usbfs can't + handle Access Control Lists (ACL) which are the default way to + grant access to USB devices for untrusted users of a desktop + system. + + The usbfs functionality is replaced by real device-nodes managed by + udev. These nodes lived in /dev/bus/usb and are used by libusb. config USB_DEVICE_CLASS bool "USB device class-devices (DEPRECATED)" -- cgit v1.2.3 From a068add5e3d99c5ca8c36da412c2156b0cb8466b Mon Sep 17 00:00:00 2001 From: Pascal Terjan Date: Thu, 16 Apr 2009 19:00:45 +0200 Subject: USB: Ignore storage device in modem mode on DWN-652 D-Link DWN-652 in Modem mode exposes 3 interfaces - First one is the USB storage one - Second one is for both control and connection - Third one is unknown This patch avoids usb-storage trying to switch again when already in modem mode, and exposes only 2 ttyUSB instead of 3 by not attaching to the storage interface Signed-off-by: Pascal Terjan Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 15 +++++++++++++++ drivers/usb/storage/option_ms.c | 3 ++- drivers/usb/storage/unusual_devs.h | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 7817b82889ca..50c2476acae0 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -43,6 +43,8 @@ #include /* Function prototypes */ +static int option_probe(struct usb_serial *serial, + const struct usb_device_id *id); static int option_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); static void option_close(struct tty_struct *tty, struct usb_serial_port *port, @@ -549,6 +551,7 @@ static struct usb_serial_driver option_1port_device = { .usb_driver = &option_driver, .id_table = option_ids, .num_ports = 1, + .probe = option_probe, .open = option_open, .close = option_close, .write = option_write, @@ -624,6 +627,18 @@ static void __exit option_exit(void) module_init(option_init); module_exit(option_exit); +static int option_probe(struct usb_serial *serial, + const struct usb_device_id *id) +{ + /* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */ + if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID && + serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 && + serial->interface->cur_altsetting->desc.bInterfaceClass == 0x8) + return -ENODEV; + + return 0; +} + static void option_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { diff --git a/drivers/usb/storage/option_ms.c b/drivers/usb/storage/option_ms.c index 353f922939a4..126ea3424c03 100644 --- a/drivers/usb/storage/option_ms.c +++ b/drivers/usb/storage/option_ms.c @@ -94,7 +94,8 @@ int option_ms_init(struct us_data *us) */ if (udev->descriptor.bDeviceClass != 0 || udev->descriptor.bDeviceSubClass != 0 || - udev->descriptor.bDeviceProtocol != 0) + udev->descriptor.bDeviceProtocol != 0 || + udev->actconfig->desc.bNumInterfaces == 3) return USB_STOR_TRANSPORT_GOOD; US_DEBUGP("Option MS: option_ms_init called\n"); diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index fa65a3b08601..bc225a8ac242 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1384,7 +1384,7 @@ UNUSUAL_DEV( 0x10d6, 0x2200, 0x0100, 0x0100, UNUSUAL_DEV( 0x1186, 0x3e04, 0x0000, 0x0000, "D-Link", "USB Mass Storage", - US_SC_DEVICE, US_PR_DEVICE, option_ms_init, 0), + US_SC_DEVICE, US_PR_DEVICE, option_ms_init, US_FL_IGNORE_DEVICE), /* Reported by Kevin Lloyd * Entry is needed for the initializer function override, -- cgit v1.2.3 From 07833cc3115c19bdb975ef014513585e9d5b3146 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 15 Apr 2009 21:34:40 -0400 Subject: USB: core/sysfs: fix sparse warnings Fix 3 sparse warning in drivers/usb/core/sysfs.c. warning: symbol '__mptr' shadows an earlier one Signed-off-by: H Hartley Sweeten Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/sysfs.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index c66789197927..b5c72e458943 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -552,8 +552,8 @@ static struct attribute *dev_string_attrs[] = { static mode_t dev_string_attrs_are_visible(struct kobject *kobj, struct attribute *a, int n) { - struct usb_device *udev = to_usb_device( - container_of(kobj, struct device, kobj)); + struct device *dev = container_of(kobj, struct device, kobj); + struct usb_device *udev = to_usb_device(dev); if (a == &dev_attr_manufacturer.attr) { if (udev->manufacturer == NULL) @@ -585,8 +585,8 @@ static ssize_t read_descriptors(struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { - struct usb_device *udev = to_usb_device( - container_of(kobj, struct device, kobj)); + struct device *dev = container_of(kobj, struct device, kobj); + struct usb_device *udev = to_usb_device(dev); size_t nleft = count; size_t srclen, n; int cfgno; @@ -786,8 +786,8 @@ static struct attribute *intf_assoc_attrs[] = { static mode_t intf_assoc_attrs_are_visible(struct kobject *kobj, struct attribute *a, int n) { - struct usb_interface *intf = to_usb_interface( - container_of(kobj, struct device, kobj)); + struct device *dev = container_of(kobj, struct device, kobj); + struct usb_interface *intf = to_usb_interface(dev); if (intf->intf_assoc == NULL) return 0; -- cgit v1.2.3 From 6cbe5545092616346dcfcbc874e2abd4392ae77a Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 15 Apr 2009 22:28:41 +0200 Subject: USB: ci13xxx_udc: use helper functions to determine endpoint type and direction Use helper functions to determine the type and direction of an endpoint instead of fiddling with bEndpointAddress and bmAttributes Signed-off-by: Matthias Kaehlcke Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/ci13xxx_udc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 38e531ecae4d..c7cb87a6fee2 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -1977,9 +1977,9 @@ static int ep_enable(struct usb_ep *ep, if (!list_empty(&mEp->qh[mEp->dir].queue)) warn("enabling a non-empty endpoint!"); - mEp->dir = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? TX : RX; - mEp->num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - mEp->type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + mEp->dir = usb_endpoint_dir_in(desc) ? TX : RX; + mEp->num = usb_endpoint_num(desc); + mEp->type = usb_endpoint_type(desc); mEp->ep.maxpacket = __constant_le16_to_cpu(desc->wMaxPacketSize); -- cgit v1.2.3 From f832383fc5169c06baab9e027f53e7a066c11879 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 15 Apr 2009 22:27:49 +0200 Subject: USB: atmel_usba_udc: use helper functions to determine endpoint type and direction Use helper functions to determine the type and direction of an endpoint instead of fiddling with bEndpointAddress and bmAttributes Signed-off-by: Matthias Kaehlcke Acked-by: David Brownell Acked-by: Haavard Skinnemoen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/atmel_usba_udc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 563d57275448..a2cd4d8de237 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -550,12 +550,12 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) DBG(DBG_HW, "%s: EPT_SIZE = %lu (maxpacket = %lu)\n", ep->ep.name, ept_cfg, maxpacket); - if ((desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { + if (usb_endpoint_dir_in(desc)) { ep->is_in = 1; ept_cfg |= USBA_EPT_DIR_IN; } - switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_CONTROL: ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL); ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE); -- cgit v1.2.3 From 8ede60f0f410c48743c9ef2538caa7d3c394ef94 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 15 Apr 2009 22:28:02 +0200 Subject: USB: at91_udc: use helper functions to determine endpoint type and direction Use helper functions to determine the type and direction of an endpoint instead of fiddling with bEndpointAddress and bmAttributes Signed-off-by: Matthias Kaehlcke Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/at91_udc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 0b2bb8f0706d..e954225cd769 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -485,7 +485,7 @@ static int at91_ep_enable(struct usb_ep *_ep, return -ESHUTDOWN; } - tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + tmp = usb_endpoint_type(desc); switch (tmp) { case USB_ENDPOINT_XFER_CONTROL: DBG("only one control endpoint\n"); @@ -517,7 +517,7 @@ ok: local_irq_save(flags); /* initialize endpoint to match this descriptor */ - ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0; + ep->is_in = usb_endpoint_dir_in(desc); ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC); ep->stopped = 0; if (ep->is_in) -- cgit v1.2.3 From df573f7da5f5df9b222e957bf4615b2e0538a7b1 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 15 Apr 2009 22:28:21 +0200 Subject: USB: Goku-S: use helper functions to determine endpoint type and direction Use helper functions to determine the type and direction of an endpoint instead of fiddling with bEndpointAddress and bmAttributes Signed-off-by: Matthias Kaehlcke Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/goku_udc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index de010c939dbb..112bb40a427c 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -110,10 +110,10 @@ goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) return -EINVAL; if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; - if (ep->num != (desc->bEndpointAddress & 0x0f)) + if (ep->num != usb_endpoint_num(desc)) return -EINVAL; - switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: break; @@ -142,7 +142,7 @@ goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) /* ep1/ep2 dma direction is chosen early; it works in the other * direction, with pio. be cautious with out-dma. */ - ep->is_in = (USB_DIR_IN & desc->bEndpointAddress) != 0; + ep->is_in = usb_endpoint_dir_in(desc); if (ep->is_in) { mode |= 1; ep->dma = (use_dma != 0) && (ep->num == UDC_MSTRD_ENDPOINT); -- cgit v1.2.3 From 48684fb2f908082cfb854aa43b6e73ce627840fa Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 15 Apr 2009 22:28:32 +0200 Subject: USB: gadgetfs: use helper functions to determine endpoint type and direction Use helper functions to determine the type and direction of an endpoint instead of fiddling with bEndpointAddress and bmAttributes Signed-off-by: Matthias Kaehlcke Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/inode.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index d20937f28a19..f420071f1de2 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -384,9 +384,8 @@ ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr) return value; /* halt any endpoint by doing a "wrong direction" i/o call */ - if (data->desc.bEndpointAddress & USB_DIR_IN) { - if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_ISOC) + if (usb_endpoint_dir_in(&data->desc)) { + if (usb_endpoint_xfer_isoc(&data->desc)) return -EINVAL; DBG (data->dev, "%s halt\n", data->name); spin_lock_irq (&data->dev->lock); @@ -428,9 +427,8 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) return value; /* halt any endpoint by doing a "wrong direction" i/o call */ - if (!(data->desc.bEndpointAddress & USB_DIR_IN)) { - if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_ISOC) + if (usb_endpoint_dir_in(&data->desc)) { + if (usb_endpoint_xfer_isoc(&data->desc)) return -EINVAL; DBG (data->dev, "%s halt\n", data->name); spin_lock_irq (&data->dev->lock); @@ -691,7 +689,7 @@ ep_aio_read(struct kiocb *iocb, const struct iovec *iov, struct ep_data *epdata = iocb->ki_filp->private_data; char *buf; - if (unlikely(epdata->desc.bEndpointAddress & USB_DIR_IN)) + if (unlikely(usb_endpoint_dir_in(&epdata->desc))) return -EINVAL; buf = kmalloc(iocb->ki_left, GFP_KERNEL); @@ -711,7 +709,7 @@ ep_aio_write(struct kiocb *iocb, const struct iovec *iov, size_t len = 0; int i = 0; - if (unlikely(!(epdata->desc.bEndpointAddress & USB_DIR_IN))) + if (unlikely(!usb_endpoint_dir_in(&epdata->desc))) return -EINVAL; buf = kmalloc(iocb->ki_left, GFP_KERNEL); -- cgit v1.2.3 From f52aefa3eb676a2ab2a35ff24ec010b905ace219 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 15 Apr 2009 22:28:28 +0200 Subject: USB: UHCI queue: use usb_endpoint_type() use usb_endpoint_type() instead of fiddling manually with bmAttributes Signed-off-by: Matthias Kaehlcke Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-q.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 3e5807d14ffb..64e57bfe236b 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -260,7 +260,7 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, INIT_LIST_HEAD(&qh->node); if (udev) { /* Normal QH */ - qh->type = hep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + qh->type = usb_endpoint_type(&hep->desc); if (qh->type != USB_ENDPOINT_XFER_ISOC) { qh->dummy_td = uhci_alloc_td(uhci); if (!qh->dummy_td) { -- cgit v1.2.3 From fd9fb8c4a4a758406b3e3e199140235dabd364e7 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 21 Apr 2009 20:34:24 -0700 Subject: USB: pxa27x_udc: introduce pxa27x_clear_otgph() Follow pxa27x change in OTGPH handling, and use the newly defined pxa27x_clear_otgph(). Signed-off-by: Robert Jarzmik Acked-by: Eric Miao Acked-by: David Brownell --- drivers/usb/gadget/pxa27x_udc.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 8cc676ecbb23..ffe6e0afc2ee 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -38,7 +38,6 @@ #include #include #include -#include /* FIXME: for PSSR */ #include #include "pxa27x_udc.h" @@ -2479,6 +2478,12 @@ static void pxa_udc_shutdown(struct platform_device *_dev) udc_disable(udc); } +#ifdef CONFIG_CPU_PXA27x +extern void pxa27x_clear_otgph(void); +#else +#define pxa27x_clear_otgph() do {} while (0) +#endif + #ifdef CONFIG_PM /** * pxa_udc_suspend - Suspend udc device @@ -2546,8 +2551,7 @@ static int pxa_udc_resume(struct platform_device *_dev) * Software must configure the USB OTG pad, UDC, and UHC * to the state they were in before entering sleep mode. */ - if (cpu_is_pxa27x()) - PSSR |= PSSR_OTGPH; + pxa27x_clear_otgph(); return 0; } -- cgit v1.2.3 From fe7448993daba0c8d024bfe3545505feddd7eab3 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Tue, 21 Apr 2009 20:34:44 -0700 Subject: USB: pxa27x_udc: compatibility with pxa320 SoC Got pxa27x_udc working on the pxa320 Nomad platform. The problem was that the pxa3xx UDC is not quite compatible with the pxa27x UDC in how it handles back-to-back control packets. The pxa27x probably drops them by default, but the pxa320 does not, and you have to detect it and set the OPC bit to clear the zero-length packet. Signed-off-by: Aric Blumer Signed-off-by: Robert Jarzmik Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 2 +- drivers/usb/gadget/pxa27x_udc.c | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 080bb1e4b847..772dd3b9bab9 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -253,7 +253,7 @@ config USB_PXA25X_SMALL config USB_GADGET_PXA27X boolean "PXA 27x" - depends on ARCH_PXA && PXA27x + depends on ARCH_PXA && (PXA27x || PXA3xx) select USB_OTG_UTILS help Intel's PXA 27x series XScale ARM v5TE processors include diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index ffe6e0afc2ee..51790b06b8ca 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -1892,6 +1892,15 @@ static void handle_ep0_ctrl_req(struct pxa_udc *udc, nuke(ep, -EPROTO); + /* + * In the PXA320 manual, in the section about Back-to-Back setup + * packets, it describes this situation. The solution is to set OPC to + * get rid of the status packet, and then continue with the setup + * packet. Generalize to pxa27x CPUs. + */ + if (epout_has_pkt(ep) && (ep_count_bytes_remain(ep) == 0)) + udc_ep_writel(ep, UDCCSR, UDCCSR0_OPC); + /* read SETUP packet */ for (i = 0; i < 2; i++) { if (unlikely(ep_is_empty(ep))) @@ -1965,6 +1974,8 @@ stall: * cleared by software. * - clearing UDCCSR0_OPC always flushes ep0. If in setup stage, never do it * before reading ep0. + * This is true only for PXA27x. This is not true anymore for PXA3xx family + * (check Back-to-Back setup packet in developers guide). * - irq can be called on a "packet complete" event (opc_irq=1), while * UDCCSR0_OPC is not yet raised (delta can be as big as 100ms * from experimentation). @@ -2575,7 +2586,7 @@ static struct platform_driver udc_driver = { static int __init udc_init(void) { - if (!cpu_is_pxa27x()) + if (!cpu_is_pxa27x() && !cpu_is_pxa3xx()) return -ENODEV; printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION); -- cgit v1.2.3 From 065a881f59eb9efc665b84e1ccdaaea85b83231b Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Tue, 21 Apr 2009 20:41:03 -0700 Subject: USB: pxa27x_udc: single-thread setup requests Since the PXA 27x UDC automatically ACK's some control packets such as SET_INTERFACE, the gadgets may not get a chance to process the request before another control packet is received. The Linux gadgets do not expect to receive setup callbacks out of order. The file storage gadget only saves the "highest" priority request. The PXA27x UDC driver must make sure it only sends one up at a time, allowing the gadget to make changes before continuing. In theory, the host would be NACK'd while the gadget processes the change but the UDC has already ACK'd the request. If another request is sent by the host that is not automatically ACK'd by the UDC, then the throttling happens properly to regain sync. The observed case was the file_storage gadget timing out on a BulkReset request because the SET_INTERFACE was being processed by the gadget. Since SET_INTERFACE is higher priority than BulkReset, the BulkReset was dropped. This was exacerbated by turning on the debug which delayed the fsg signal processing thread. This also fixes the "should never get in WAIT_ACK_SET_CONF_INTERF state here!!!" warning. Reported-by: Vernon Sauder Signed-off-by: Robert Jarzmik Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman index 51790b0..1937d8c 100644 --- drivers/usb/gadget/pxa27x_udc.c | 50 ++++++++++++++++++++++++++++------------- drivers/usb/gadget/pxa27x_udc.h | 2 ++ 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 51790b06b8ca..1937d8c7b433 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -472,6 +472,23 @@ static inline void udc_clear_mask_UDCCR(struct pxa_udc *udc, int mask) (udccr & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS)); } +/** + * ep_write_UDCCSR - set bits in UDCCSR + * @udc: udc device + * @mask: bits to set in UDCCR + * + * Sets bits in UDCCSR (UDCCSR0 and UDCCSR*). + * + * A specific case is applied to ep0 : the ACM bit is always set to 1, for + * SET_INTERFACE and SET_CONFIGURATION. + */ +static inline void ep_write_UDCCSR(struct pxa_ep *ep, int mask) +{ + if (is_ep0(ep)) + mask |= UDCCSR0_ACM; + udc_ep_writel(ep, UDCCSR, mask); +} + /** * ep_count_bytes_remain - get how many bytes in udc endpoint * @ep: udc endpoint @@ -860,7 +877,7 @@ static int read_packet(struct pxa_ep *ep, struct pxa27x_request *req) *buf++ = udc_ep_readl(ep, UDCDR); req->req.actual += count; - udc_ep_writel(ep, UDCCSR, UDCCSR_PC); + ep_write_UDCCSR(ep, UDCCSR_PC); return count; } @@ -968,12 +985,12 @@ static int write_fifo(struct pxa_ep *ep, struct pxa27x_request *req) if (udccsr & UDCCSR_PC) { ep_vdbg(ep, "Clearing Transmit Complete, udccsr=%x\n", udccsr); - udc_ep_writel(ep, UDCCSR, UDCCSR_PC); + ep_write_UDCCSR(ep, UDCCSR_PC); } if (udccsr & UDCCSR_TRN) { ep_vdbg(ep, "Clearing Underrun on, udccsr=%x\n", udccsr); - udc_ep_writel(ep, UDCCSR, UDCCSR_TRN); + ep_write_UDCCSR(ep, UDCCSR_TRN); } count = write_packet(ep, req, max); @@ -995,7 +1012,7 @@ static int write_fifo(struct pxa_ep *ep, struct pxa27x_request *req) } if (is_short) - udc_ep_writel(ep, UDCCSR, UDCCSR_SP); + ep_write_UDCCSR(ep, UDCCSR_SP); /* requests complete when all IN data is in the FIFO */ if (is_last) { @@ -1028,7 +1045,7 @@ static int read_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req) while (epout_has_pkt(ep)) { count = read_packet(ep, req); - udc_ep_writel(ep, UDCCSR, UDCCSR0_OPC); + ep_write_UDCCSR(ep, UDCCSR0_OPC); inc_ep_stats_bytes(ep, count, !USB_DIR_IN); is_short = (count < ep->fifo_size); @@ -1073,7 +1090,7 @@ static int write_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req) /* Sends either a short packet or a 0 length packet */ if (unlikely(is_short)) - udc_ep_writel(ep, UDCCSR, UDCCSR0_IPR); + ep_write_UDCCSR(ep, UDCCSR0_IPR); ep_dbg(ep, "in %d bytes%s%s, %d left, req=%p, udccsr0=0x%03x\n", count, is_short ? "/S" : "", is_last ? "/L" : "", @@ -1276,7 +1293,7 @@ static int pxa_ep_set_halt(struct usb_ep *_ep, int value) /* FST, FEF bits are the same for control and non control endpoints */ rc = 0; - udc_ep_writel(ep, UDCCSR, UDCCSR_FST | UDCCSR_FEF); + ep_write_UDCCSR(ep, UDCCSR_FST | UDCCSR_FEF); if (is_ep0(ep)) set_ep0state(ep->dev, STALL); @@ -1342,7 +1359,7 @@ static void pxa_ep_fifo_flush(struct usb_ep *_ep) udc_ep_readl(ep, UDCDR); } else { /* most IN status is the same, but ISO can't stall */ - udc_ep_writel(ep, UDCCSR, + ep_write_UDCCSR(ep, UDCCSR_PC | UDCCSR_FEF | UDCCSR_TRN | (EPXFERTYPE_is_ISO(ep) ? 0 : UDCCSR_SST)); } @@ -1727,6 +1744,7 @@ static void udc_enable(struct pxa_udc *udc) memset(&udc->stats, 0, sizeof(udc->stats)); udc_set_mask_UDCCR(udc, UDCCR_UDE); + ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_ACM); udelay(2); if (udc_readl(udc, UDCCR) & UDCCR_EMCE) dev_err(udc->dev, "Configuration errors, udc disabled\n"); @@ -1899,7 +1917,7 @@ static void handle_ep0_ctrl_req(struct pxa_udc *udc, * packet. Generalize to pxa27x CPUs. */ if (epout_has_pkt(ep) && (ep_count_bytes_remain(ep) == 0)) - udc_ep_writel(ep, UDCCSR, UDCCSR0_OPC); + ep_write_UDCCSR(ep, UDCCSR0_OPC); /* read SETUP packet */ for (i = 0; i < 2; i++) { @@ -1927,7 +1945,7 @@ static void handle_ep0_ctrl_req(struct pxa_udc *udc, set_ep0state(udc, OUT_DATA_STAGE); /* Tell UDC to enter Data Stage */ - udc_ep_writel(ep, UDCCSR, UDCCSR0_SA | UDCCSR0_OPC); + ep_write_UDCCSR(ep, UDCCSR0_SA | UDCCSR0_OPC); i = udc->driver->setup(&udc->gadget, &u.r); if (i < 0) @@ -1937,7 +1955,7 @@ out: stall: ep_dbg(ep, "protocol STALL, udccsr0=%03x err %d\n", udc_ep_readl(ep, UDCCSR), i); - udc_ep_writel(ep, UDCCSR, UDCCSR0_FST | UDCCSR0_FTF); + ep_write_UDCCSR(ep, UDCCSR0_FST | UDCCSR0_FTF); set_ep0state(udc, STALL); goto out; } @@ -2008,7 +2026,7 @@ static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq) if (udccsr0 & UDCCSR0_SST) { ep_dbg(ep, "clearing stall status\n"); nuke(ep, -EPIPE); - udc_ep_writel(ep, UDCCSR, UDCCSR0_SST); + ep_write_UDCCSR(ep, UDCCSR0_SST); ep0_idle(udc); } @@ -2033,7 +2051,7 @@ static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq) break; case IN_DATA_STAGE: /* GET_DESCRIPTOR */ if (epout_has_pkt(ep)) - udc_ep_writel(ep, UDCCSR, UDCCSR0_OPC); + ep_write_UDCCSR(ep, UDCCSR0_OPC); if (req && !ep_is_full(ep)) completed = write_ep0_fifo(ep, req); if (completed) @@ -2046,7 +2064,7 @@ static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq) ep0_end_out_req(ep, req); break; case STALL: - udc_ep_writel(ep, UDCCSR, UDCCSR0_FST); + ep_write_UDCCSR(ep, UDCCSR0_FST); break; case IN_STATUS_STAGE: /* @@ -2141,6 +2159,7 @@ static void pxa27x_change_configuration(struct pxa_udc *udc, int config) set_ep0state(udc, WAIT_ACK_SET_CONF_INTERF); udc->driver->setup(&udc->gadget, &req); + ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_AREN); } /** @@ -2169,6 +2188,7 @@ static void pxa27x_change_interface(struct pxa_udc *udc, int iface, int alt) set_ep0state(udc, WAIT_ACK_SET_CONF_INTERF); udc->driver->setup(&udc->gadget, &req); + ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_AREN); } /* @@ -2290,7 +2310,7 @@ static void irq_udc_reset(struct pxa_udc *udc) memset(&udc->stats, 0, sizeof udc->stats); nuke(ep, -EPROTO); - udc_ep_writel(ep, UDCCSR, UDCCSR0_FTF | UDCCSR0_OPC); + ep_write_UDCCSR(ep, UDCCSR0_FTF | UDCCSR0_OPC); ep0_idle(udc); } diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h index db58125331da..e25225e26586 100644 --- a/drivers/usb/gadget/pxa27x_udc.h +++ b/drivers/usb/gadget/pxa27x_udc.h @@ -130,6 +130,8 @@ #define UP2OCR_HXOE (1 << 17) /* Transceiver Output Enable */ #define UP2OCR_SEOS (1 << 24) /* Single-Ended Output Select */ +#define UDCCSR0_ACM (1 << 9) /* Ack Control Mode */ +#define UDCCSR0_AREN (1 << 8) /* Ack Response Enable */ #define UDCCSR0_SA (1 << 7) /* Setup Active */ #define UDCCSR0_RNE (1 << 6) /* Receive FIFO Not Empty */ #define UDCCSR0_FST (1 << 5) /* Force Stall */ -- cgit v1.2.3