diff options
Diffstat (limited to 'drivers/usb/musb/musb_host.c')
| -rw-r--r-- | drivers/usb/musb/musb_host.c | 216 |
1 files changed, 85 insertions, 131 deletions
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 76decb8011eb..6b4481a867c5 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MUSB OTG driver host support * @@ -5,32 +6,6 @@ * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #include <linux/module.h> @@ -38,6 +13,7 @@ #include <linux/delay.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/errno.h> #include <linux/list.h> #include <linux/dma-mapping.h> @@ -139,6 +115,7 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep) "Could not flush host TX%d fifo: csr: %04x\n", ep->epnum, csr)) return; + mdelay(1); } } @@ -219,7 +196,6 @@ static struct musb_qh *musb_ep_get_qh(struct musb_hw_ep *ep, int is_in) static void musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) { - u16 frame; u32 len; void __iomem *mbase = musb->mregs; struct urb *urb = next_urb(qh); @@ -268,7 +244,6 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_INT: musb_dbg(musb, "check whether there's still time for periodic Tx"); - frame = musb_readw(mbase, MUSB_FRAME); /* FIXME this doesn't implement that scheduling policy ... * or handle framecounter wrapping */ @@ -312,26 +287,6 @@ __acquires(musb->lock) spin_lock(&musb->lock); } -/* For bulk/interrupt endpoints only */ -static inline void musb_save_toggle(struct musb_qh *qh, int is_in, - struct urb *urb) -{ - void __iomem *epio = qh->hw_ep->regs; - u16 csr; - - /* - * FIXME: the current Mentor DMA code seems to have - * problems getting toggle correct. - */ - - if (is_in) - csr = musb_readw(epio, MUSB_RXCSR) & MUSB_RXCSR_H_DATATOGGLE; - else - csr = musb_readw(epio, MUSB_TXCSR) & MUSB_TXCSR_H_DATATOGGLE; - - usb_settoggle(urb->dev, qh->epnum, !is_in, csr ? 1 : 0); -} - /* * Advance this hardware endpoint's queue, completing the specified URB and * advancing to either the next URB queued to that qh, or else invalidating @@ -346,6 +301,7 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb, struct musb_hw_ep *ep = qh->hw_ep; int ready = qh->is_ready; int status; + u16 toggle; status = (urb->status == -EINPROGRESS) ? 0 : urb->status; @@ -353,7 +309,8 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb, switch (qh->type) { case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: - musb_save_toggle(qh, is_in, urb); + toggle = musb->io.get_toggle(qh, !is_in); + usb_settoggle(urb->dev, qh->epnum, !is_in, toggle ? 1 : 0); break; case USB_ENDPOINT_XFER_ISOC: if (status == 0 && urb->error_count) @@ -365,10 +322,16 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb, musb_giveback(musb, urb, status); qh->is_ready = ready; + /* + * musb->lock had been unlocked in musb_giveback, so qh may + * be freed, need to get it again + */ + qh = musb_ep_get_qh(hw_ep, is_in); + /* reclaim resources (and bandwidth) ASAP; deschedule it, and * invalidate qh as soon as list_empty(&hep->urb_list) */ - if (list_empty(&qh->hep->urb_list)) { + if (qh && list_empty(&qh->hep->urb_list)) { struct list_head *head; struct dma_controller *dma = musb->dma_controller; @@ -404,6 +367,7 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb, qh = first_qh(head); break; } + fallthrough; case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_INT: @@ -417,13 +381,7 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb, } } - /* - * The pipe must be broken if current urb->status is set, so don't - * start next urb. - * TODO: to minimize the risk of regression, only check urb->status - * for RX, until we have a test case to understand the behavior of TX. - */ - if ((!status || !is_in) && qh && qh->is_ready) { + if (qh != NULL && qh->is_ready) { musb_dbg(musb, "... next ep%d %cX urb %p", hw_ep->epnum, is_in ? 'R' : 'T', next_urb(qh)); musb_start_urb(musb, is_in, qh); @@ -606,19 +564,15 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, u8 epnum) /* Set RXMAXP with the FIFO size of the endpoint * to disable double buffer mode. */ - if (musb->double_buffer_not_ok) - musb_writew(ep->regs, MUSB_RXMAXP, ep->max_packet_sz_rx); - else - musb_writew(ep->regs, MUSB_RXMAXP, - qh->maxpacket | ((qh->hb_mult - 1) << 11)); + musb_writew(ep->regs, MUSB_RXMAXP, + qh->maxpacket | ((qh->hb_mult - 1) << 11)); ep->rx_reinit = 0; } -static void musb_tx_dma_set_mode_mentor(struct dma_controller *dma, - struct musb_hw_ep *hw_ep, struct musb_qh *qh, - struct urb *urb, u32 offset, - u32 *length, u8 *mode) +static void musb_tx_dma_set_mode_mentor(struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + u32 *length, u8 *mode) { struct dma_channel *channel = hw_ep->tx_channel; void __iomem *epio = hw_ep->regs; @@ -654,12 +608,8 @@ static void musb_tx_dma_set_mode_mentor(struct dma_controller *dma, musb_writew(epio, MUSB_TXCSR, csr); } -static void musb_tx_dma_set_mode_cppi_tusb(struct dma_controller *dma, - struct musb_hw_ep *hw_ep, - struct musb_qh *qh, +static void musb_tx_dma_set_mode_cppi_tusb(struct musb_hw_ep *hw_ep, struct urb *urb, - u32 offset, - u32 *length, u8 *mode) { struct dma_channel *channel = hw_ep->tx_channel; @@ -682,11 +632,10 @@ static bool musb_tx_dma_program(struct dma_controller *dma, u8 mode; if (musb_dma_inventra(hw_ep->musb) || musb_dma_ux500(hw_ep->musb)) - musb_tx_dma_set_mode_mentor(dma, hw_ep, qh, urb, offset, + musb_tx_dma_set_mode_mentor(hw_ep, qh, &length, &mode); else if (is_cppi_enabled(hw_ep->musb) || tusb_dma_omap(hw_ep->musb)) - musb_tx_dma_set_mode_cppi_tusb(dma, hw_ep, qh, urb, offset, - &length, &mode); + musb_tx_dma_set_mode_cppi_tusb(hw_ep, urb, &mode); else return false; @@ -806,13 +755,8 @@ static void musb_ep_program(struct musb *musb, u8 epnum, ); csr |= MUSB_TXCSR_MODE; - if (!hw_ep->tx_double_buffered) { - if (usb_gettoggle(urb->dev, qh->epnum, 1)) - csr |= MUSB_TXCSR_H_WR_DATATOGGLE - | MUSB_TXCSR_H_DATATOGGLE; - else - csr |= MUSB_TXCSR_CLRDATATOG; - } + if (!hw_ep->tx_double_buffered) + csr |= musb->io.set_toggle(qh, is_out, urb); musb_writew(epio, MUSB_TXCSR, csr); /* REVISIT may need to clear FLUSHFIFO ... */ @@ -836,10 +780,7 @@ static void musb_ep_program(struct musb *musb, u8 epnum, /* protocol/endpoint/interval/NAKlimit */ if (epnum) { musb_writeb(epio, MUSB_TXTYPE, qh->type_reg); - if (musb->double_buffer_not_ok) { - musb_writew(epio, MUSB_TXMAXP, - hw_ep->max_packet_sz_tx); - } else if (can_bulk_split(musb, qh->type)) { + if (can_bulk_split(musb, qh->type)) { qh->hb_mult = hw_ep->max_packet_sz_tx / packet_sz; musb_writew(epio, MUSB_TXMAXP, packet_sz @@ -858,10 +799,9 @@ static void musb_ep_program(struct musb *musb, u8 epnum, } if (can_bulk_split(musb, qh->type)) - load_count = min((u32) hw_ep->max_packet_sz_tx, - len); + load_count = min_t(u32, hw_ep->max_packet_sz_tx, len); else - load_count = min((u32) packet_sz, len); + load_count = min_t(u32, packet_sz, len); if (dma_channel && musb_tx_dma_program(dma_controller, hw_ep, qh, urb, offset, len)) @@ -897,17 +837,12 @@ finish: /* IN/receive */ } else { - u16 csr; + u16 csr = 0; if (hw_ep->rx_reinit) { musb_rx_reinit(musb, qh, epnum); + csr |= musb->io.set_toggle(qh, is_out, urb); - /* init new state: toggle and NYET, maybe DMA later */ - if (usb_gettoggle(urb->dev, qh->epnum, 0)) - csr = MUSB_RXCSR_H_WR_DATATOGGLE - | MUSB_RXCSR_H_DATATOGGLE; - else - csr = 0; if (qh->type == USB_ENDPOINT_XFER_INT) csr |= MUSB_RXCSR_DISNYET; @@ -970,6 +905,7 @@ static void musb_bulk_nak_timeout(struct musb *musb, struct musb_hw_ep *ep, void __iomem *epio = ep->regs; struct musb_qh *cur_qh, *next_qh; u16 rx_csr, tx_csr; + u16 toggle; musb_ep_select(mbase, ep->epnum); if (is_in) { @@ -1007,7 +943,8 @@ static void musb_bulk_nak_timeout(struct musb *musb, struct musb_hw_ep *ep, urb->actual_length += dma->actual_len; dma->actual_len = 0L; } - musb_save_toggle(cur_qh, is_in, urb); + toggle = musb->io.get_toggle(cur_qh, !is_in); + usb_settoggle(urb->dev, cur_qh->epnum, !is_in, toggle ? 1 : 0); if (is_in) { /* move cur_qh to end of queue */ @@ -1028,7 +965,9 @@ static void musb_bulk_nak_timeout(struct musb *musb, struct musb_hw_ep *ep, /* set tx_reinit and schedule the next qh */ ep->tx_reinit = 1; } - musb_start_urb(musb, is_in, next_qh); + + if (next_qh) + musb_start_urb(musb, is_in, next_qh); } } @@ -1080,7 +1019,7 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb) musb->ep0_stage = MUSB_EP0_OUT; more = true; } - /* FALLTHROUGH */ + fallthrough; case MUSB_EP0_OUT: fifo_count = min_t(size_t, qh->maxpacket, urb->transfer_buffer_length - @@ -1090,7 +1029,7 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb) + urb->actual_length); musb_dbg(musb, "Sending %d byte%s to ep0 fifo %p", fifo_count, - (fifo_count == 1) ? "" : "s", + str_plural(fifo_count), fifo_dest); musb_write_fifo(hw_ep, fifo_count, fifo_dest); @@ -1318,7 +1257,7 @@ void musb_host_tx(struct musb *musb, u8 epnum) MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY); } - return; + return; } done: @@ -1497,10 +1436,7 @@ done: * We need to map sg if the transfer_buffer is * NULL. */ - if (!urb->transfer_buffer) - qh->use_sg = true; - - if (qh->use_sg) { + if (!urb->transfer_buffer) { /* sg_miter_start is already done in musb_ep_program */ if (!sg_miter_next(&qh->sg_miter)) { dev_err(musb->controller, "error: sg list empty\n"); @@ -1508,9 +1444,8 @@ done: status = -EINVAL; goto done; } - urb->transfer_buffer = qh->sg_miter.addr; length = min_t(u32, length, qh->sg_miter.length); - musb_write_fifo(hw_ep, length, urb->transfer_buffer); + musb_write_fifo(hw_ep, length, qh->sg_miter.addr); qh->sg_miter.consumed = length; sg_miter_stop(&qh->sg_miter); } else { @@ -1519,11 +1454,6 @@ done: qh->segsize = length; - if (qh->use_sg) { - if (offset + length >= urb->transfer_buffer_length) - qh->use_sg = false; - } - musb_ep_select(mbase, epnum); musb_writew(epio, MUSB_TXCSR, MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY); @@ -1805,7 +1735,6 @@ void musb_host_rx(struct musb *musb, u8 epnum) struct musb_qh *qh = hw_ep->in_qh; size_t xfer_len; void __iomem *mbase = musb->mregs; - int pipe; u16 rx_csr, val; bool iso_err = false; bool done = false; @@ -1834,8 +1763,6 @@ void musb_host_rx(struct musb *musb, u8 epnum) return; } - pipe = urb->pipe; - trace_musb_urb_rx(musb, urb); /* check for errors, concurrent stall & unlink is not really @@ -1847,9 +1774,15 @@ void musb_host_rx(struct musb *musb, u8 epnum) status = -EPIPE; } else if (rx_csr & MUSB_RXCSR_H_ERROR) { - musb_dbg(musb, "end %d RX proto error", epnum); + dev_err(musb->controller, "ep%d RX three-strikes error", epnum); - status = -EPROTO; + /* + * The three-strikes error could only happen when the USB + * device is not accessible, for example detached or powered + * off. So return the fatal error -ESHUTDOWN so hopefully the + * USB device drivers won't immediately resubmit the same URB. + */ + status = -ESHUTDOWN; musb_writeb(epio, MUSB_RXINTERVAL, 0); rx_csr &= ~MUSB_RXCSR_H_ERROR; @@ -2041,8 +1974,10 @@ finish: urb->actual_length += xfer_len; qh->offset += xfer_len; if (done) { - if (qh->use_sg) + if (qh->use_sg) { qh->use_sg = false; + urb->transfer_buffer = NULL; + } if (urb->status == -EINPROGRESS) urb->status = status; @@ -2151,6 +2086,10 @@ static int musb_schedule( (USB_SPEED_HIGH == qh->dev->speed) ? 8 : 4; goto success; } else if (best_end < 0) { + dev_err(musb->controller, + "%s hwep alloc failed for %dx%d\n", + musb_ep_xfertype_string(qh->type), + qh->hb_mult, qh->maxpacket); return -ENOSPC; } @@ -2243,6 +2182,10 @@ static int musb_urb_enqueue( ok = (usb_pipein(urb->pipe) && musb->hb_iso_rx) || (usb_pipeout(urb->pipe) && musb->hb_iso_tx); if (!ok) { + dev_err(musb->controller, + "high bandwidth %s (%dx%d) not supported\n", + musb_ep_xfertype_string(qh->type), + qh->hb_mult, qh->maxpacket & 0x7ff); ret = -EMSGSIZE; goto done; } @@ -2279,7 +2222,7 @@ static int musb_urb_enqueue( interval = max_t(u8, epd->bInterval, 1); break; } - /* FALLTHROUGH */ + fallthrough; case USB_ENDPOINT_XFER_ISOC: /* ISO always uses logarithmic encoding */ interval = min_t(u8, epd->bInterval, 16); @@ -2461,6 +2404,7 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) * and its URB list has emptied, recycle this qh. */ if (ready && list_empty(&qh->hep->urb_list)) { + musb_ep_set_qh(qh->hw_ep, is_in, NULL); qh->hep->hcpriv = NULL; list_del(&qh->ring); kfree(qh); @@ -2555,13 +2499,16 @@ static int musb_bus_suspend(struct usb_hcd *hcd) { struct musb *musb = hcd_to_musb(hcd); u8 devctl; + int ret; - musb_port_suspend(musb, true); + ret = musb_port_suspend(musb, true); + if (ret) + return ret; if (!is_host_active(musb)) return 0; - switch (musb->xceiv->otg->state) { + switch (musb_get_state(musb)) { case OTG_STATE_A_SUSPEND: return 0; case OTG_STATE_A_WAIT_VRISE: @@ -2571,7 +2518,7 @@ static int musb_bus_suspend(struct usb_hcd *hcd) */ devctl = musb_readb(musb->mregs, MUSB_DEVCTL); if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) - musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON; + musb_set_state(musb, OTG_STATE_A_WAIT_BCON); break; default: break; @@ -2579,7 +2526,7 @@ static int musb_bus_suspend(struct usb_hcd *hcd) if (musb->is_active) { WARNING("trying to suspend as %s while active\n", - usb_otg_state_string(musb->xceiv->otg->state)); + musb_otg_state_string(musb)); return -EBUSY; } else return 0; @@ -2603,7 +2550,7 @@ static int musb_bus_resume(struct usb_hcd *hcd) struct musb_temp_buffer { void *kmalloc_ptr; void *old_xfer_buffer; - u8 data[0]; + u8 data[]; }; static void musb_free_temp_buffer(struct urb *urb) @@ -2716,7 +2663,7 @@ static const struct hc_driver musb_hc_driver = { .description = "musb-hcd", .product_desc = "MUSB HDRC host driver", .hcd_priv_size = sizeof(struct musb *), - .flags = HCD_USB2 | HCD_MEMORY, + .flags = HCD_USB2 | HCD_DMA | HCD_MEMORY, /* not using irq handler or reset hooks from usbcore, since * those must be shared with peripheral code for OTG configs @@ -2763,7 +2710,7 @@ int musb_host_alloc(struct musb *musb) void musb_host_cleanup(struct musb *musb) { - if (musb->port_mode == MUSB_PORT_MODE_GADGET) + if (musb->port_mode == MUSB_PERIPHERAL) return; usb_remove_hcd(musb->hcd); } @@ -2778,15 +2725,22 @@ int musb_host_setup(struct musb *musb, int power_budget) int ret; struct usb_hcd *hcd = musb->hcd; - if (musb->port_mode == MUSB_PORT_MODE_HOST) { + if (musb->port_mode == MUSB_HOST) { MUSB_HST_MODE(musb); - musb->xceiv->otg->default_a = 1; - musb->xceiv->otg->state = OTG_STATE_A_IDLE; + musb_set_state(musb, OTG_STATE_A_IDLE); + } + + if (musb->xceiv) { + otg_set_host(musb->xceiv->otg, &hcd->self); + musb->xceiv->otg->host = &hcd->self; + } else { + phy_set_mode(musb->phy, PHY_MODE_USB_HOST); } - otg_set_host(musb->xceiv->otg, &hcd->self); - hcd->self.otg_port = 1; - musb->xceiv->otg->host = &hcd->self; + + /* don't support otg protocols */ + hcd->self.otg_port = 0; hcd->power_budget = 2 * (power_budget ? : 250); + hcd->skip_phy_initialization = 1; ret = usb_add_hcd(hcd, 0, 0); if (ret < 0) |
