diff options
Diffstat (limited to 'kernel/drivers/usb/dwc2/hcd.c')
-rw-r--r-- | kernel/drivers/usb/dwc2/hcd.c | 500 |
1 files changed, 346 insertions, 154 deletions
diff --git a/kernel/drivers/usb/dwc2/hcd.c b/kernel/drivers/usb/dwc2/hcd.c index fbbbac215..571c21727 100644 --- a/kernel/drivers/usb/dwc2/hcd.c +++ b/kernel/drivers/usb/dwc2/hcd.c @@ -80,10 +80,10 @@ static void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg, if (chan == NULL) return; - hcchar = readl(hsotg->regs + HCCHAR(chan->hc_num)); - hcsplt = readl(hsotg->regs + HCSPLT(chan->hc_num)); - hctsiz = readl(hsotg->regs + HCTSIZ(chan->hc_num)); - hc_dma = readl(hsotg->regs + HCDMA(chan->hc_num)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chan->hc_num)); + hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chan->hc_num)); + hc_dma = dwc2_readl(hsotg->regs + HCDMA(chan->hc_num)); dev_dbg(hsotg->dev, " Assigned to channel %p:\n", chan); dev_dbg(hsotg->dev, " hcchar 0x%08x, hcsplt 0x%08x\n", @@ -134,7 +134,7 @@ static void dwc2_kill_urbs_in_qh_list(struct dwc2_hsotg *hsotg, list_for_each_entry_safe(qh, qh_tmp, qh_list, qh_list_entry) { list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) { - dwc2_host_complete(hsotg, qtd, -ETIMEDOUT); + dwc2_host_complete(hsotg, qtd, -ECONNRESET); dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); } } @@ -207,7 +207,7 @@ void dwc2_hcd_start(struct dwc2_hsotg *hsotg) */ hprt0 = dwc2_read_hprt0(hsotg); hprt0 |= HPRT0_RST; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); } queue_delayed_work(hsotg->wq_otg, &hsotg->start_work, @@ -228,11 +228,11 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg) channel = hsotg->hc_ptr_array[i]; if (!list_empty(&channel->hc_list_entry)) continue; - hcchar = readl(hsotg->regs + HCCHAR(i)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); if (hcchar & HCCHAR_CHENA) { hcchar &= ~(HCCHAR_CHENA | HCCHAR_EPDIR); hcchar |= HCCHAR_CHDIS; - writel(hcchar, hsotg->regs + HCCHAR(i)); + dwc2_writel(hcchar, hsotg->regs + HCCHAR(i)); } } } @@ -241,11 +241,11 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg) channel = hsotg->hc_ptr_array[i]; if (!list_empty(&channel->hc_list_entry)) continue; - hcchar = readl(hsotg->regs + HCCHAR(i)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); if (hcchar & HCCHAR_CHENA) { /* Halt the channel */ hcchar |= HCCHAR_CHDIS; - writel(hcchar, hsotg->regs + HCCHAR(i)); + dwc2_writel(hcchar, hsotg->regs + HCCHAR(i)); } dwc2_hc_cleanup(hsotg, channel); @@ -287,11 +287,11 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) * interrupt mask and status bits and disabling subsequent host * channel interrupts. */ - intr = readl(hsotg->regs + GINTMSK); + intr = dwc2_readl(hsotg->regs + GINTMSK); intr &= ~(GINTSTS_NPTXFEMP | GINTSTS_PTXFEMP | GINTSTS_HCHINT); - writel(intr, hsotg->regs + GINTMSK); + dwc2_writel(intr, hsotg->regs + GINTMSK); intr = GINTSTS_NPTXFEMP | GINTSTS_PTXFEMP | GINTSTS_HCHINT; - writel(intr, hsotg->regs + GINTSTS); + dwc2_writel(intr, hsotg->regs + GINTSTS); /* * Turn off the vbus power only if the core has transitioned to device @@ -301,7 +301,7 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) if (dwc2_is_device_mode(hsotg)) { if (hsotg->op_state != OTG_STATE_A_SUSPEND) { dev_dbg(hsotg->dev, "Disconnect: PortPower off\n"); - writel(0, hsotg->regs + HPRT0); + dwc2_writel(0, hsotg->regs + HPRT0); } dwc2_disable_host_interrupts(hsotg); @@ -324,12 +324,13 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) */ static void dwc2_hcd_rem_wakeup(struct dwc2_hsotg *hsotg) { - if (hsotg->lx_state == DWC2_L2) { + if (hsotg->bus_suspended) { hsotg->flags.b.port_suspend_change = 1; usb_hcd_resume_root_hub(hsotg->priv); - } else { - hsotg->flags.b.port_l1_change = 1; } + + if (hsotg->lx_state == DWC2_L1) + hsotg->flags.b.port_l1_change = 1; } /** @@ -354,15 +355,14 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg) /* Turn off the vbus power */ dev_dbg(hsotg->dev, "PortPower off\n"); - writel(0, hsotg->regs + HPRT0); + dwc2_writel(0, hsotg->regs + HPRT0); } +/* Caller must hold driver lock */ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, - struct dwc2_hcd_urb *urb, void **ep_handle, - gfp_t mem_flags) + struct dwc2_hcd_urb *urb, struct dwc2_qh *qh, + struct dwc2_qtd *qtd) { - struct dwc2_qtd *qtd; - unsigned long flags; u32 intr_mask; int retval; int dev_speed; @@ -379,29 +379,26 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, if ((dev_speed == USB_SPEED_LOW) && (hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED) && (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI)) { - u32 hprt0 = readl(hsotg->regs + HPRT0); + u32 hprt0 = dwc2_readl(hsotg->regs + HPRT0); u32 prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; if (prtspd == HPRT0_SPD_FULL_SPEED) return -ENODEV; } - qtd = kzalloc(sizeof(*qtd), mem_flags); if (!qtd) - return -ENOMEM; + return -EINVAL; dwc2_hcd_qtd_init(qtd, urb); - retval = dwc2_hcd_qtd_add(hsotg, qtd, (struct dwc2_qh **)ep_handle, - mem_flags); + retval = dwc2_hcd_qtd_add(hsotg, qtd, qh); if (retval) { dev_err(hsotg->dev, "DWC OTG HCD URB Enqueue failed adding QTD. Error status %d\n", retval); - kfree(qtd); return retval; } - intr_mask = readl(hsotg->regs + GINTMSK); + intr_mask = dwc2_readl(hsotg->regs + GINTMSK); if (!(intr_mask & GINTSTS_SOF)) { enum dwc2_transaction_type tr_type; @@ -413,11 +410,9 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, */ return 0; - spin_lock_irqsave(&hsotg->lock, flags); tr_type = dwc2_hcd_select_transactions(hsotg); if (tr_type != DWC2_TRANSACTION_NONE) dwc2_hcd_queue_transactions(hsotg, tr_type); - spin_unlock_irqrestore(&hsotg->lock, flags); } return 0; @@ -721,9 +716,7 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, /* 3072 = 3 max-size Isoc packets */ buf_size = 3072; - qh->dw_align_buf = dma_alloc_coherent(hsotg->dev, buf_size, - &qh->dw_align_buf_dma, - GFP_ATOMIC); + qh->dw_align_buf = kmalloc(buf_size, GFP_ATOMIC | GFP_DMA); if (!qh->dw_align_buf) return -ENOMEM; qh->dw_align_buf_size = buf_size; @@ -748,6 +741,15 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, } } + qh->dw_align_buf_dma = dma_map_single(hsotg->dev, + qh->dw_align_buf, qh->dw_align_buf_size, + chan->ep_is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) { + dev_err(hsotg->dev, "can't map align_buf\n"); + chan->align_buf = 0; + return -EINVAL; + } + chan->align_buf = qh->dw_align_buf_dma; return 0; } @@ -1069,7 +1071,7 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg) if (dbg_perio()) dev_vdbg(hsotg->dev, "Queue periodic transactions\n"); - tx_status = readl(hsotg->regs + HPTXSTS); + tx_status = dwc2_readl(hsotg->regs + HPTXSTS); qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT; fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> @@ -1084,7 +1086,7 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg) qh_ptr = hsotg->periodic_sched_assigned.next; while (qh_ptr != &hsotg->periodic_sched_assigned) { - tx_status = readl(hsotg->regs + HPTXSTS); + tx_status = dwc2_readl(hsotg->regs + HPTXSTS); qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT; if (qspcavail == 0) { @@ -1144,7 +1146,7 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg) } if (hsotg->core_params->dma_enable <= 0) { - tx_status = readl(hsotg->regs + HPTXSTS); + tx_status = dwc2_readl(hsotg->regs + HPTXSTS); qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT; fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> @@ -1167,9 +1169,9 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg) * level to ensure that new requests are loaded as * soon as possible.) */ - gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk |= GINTSTS_PTXFEMP; - writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); } else { /* * Disable the Tx FIFO empty interrupt since there are @@ -1178,9 +1180,9 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg) * handlers to queue more transactions as transfer * states change. */ - gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk &= ~GINTSTS_PTXFEMP; - writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); } } } @@ -1209,7 +1211,7 @@ static void dwc2_process_non_periodic_channels(struct dwc2_hsotg *hsotg) dev_vdbg(hsotg->dev, "Queue non-periodic transactions\n"); - tx_status = readl(hsotg->regs + GNPTXSTS); + tx_status = dwc2_readl(hsotg->regs + GNPTXSTS); qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT; fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> @@ -1232,7 +1234,7 @@ static void dwc2_process_non_periodic_channels(struct dwc2_hsotg *hsotg) * available in the request queue or the Tx FIFO */ do { - tx_status = readl(hsotg->regs + GNPTXSTS); + tx_status = dwc2_readl(hsotg->regs + GNPTXSTS); qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT; if (hsotg->core_params->dma_enable <= 0 && qspcavail == 0) { @@ -1269,7 +1271,7 @@ next: } while (hsotg->non_periodic_qh_ptr != orig_qh_ptr); if (hsotg->core_params->dma_enable <= 0) { - tx_status = readl(hsotg->regs + GNPTXSTS); + tx_status = dwc2_readl(hsotg->regs + GNPTXSTS); qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT; fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> @@ -1289,9 +1291,9 @@ next: * level to ensure that new requests are loaded as * soon as possible.) */ - gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk |= GINTSTS_NPTXFEMP; - writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); } else { /* * Disable the Tx FIFO empty interrupt since there are @@ -1300,9 +1302,9 @@ next: * handlers to queue more transactions as transfer * states change. */ - gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk &= ~GINTSTS_NPTXFEMP; - writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); } } } @@ -1340,10 +1342,10 @@ void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg, * Ensure NP Tx FIFO empty interrupt is disabled when * there are no non-periodic transfers to process */ - u32 gintmsk = readl(hsotg->regs + GINTMSK); + u32 gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk &= ~GINTSTS_NPTXFEMP; - writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); } } } @@ -1354,10 +1356,11 @@ static void dwc2_conn_id_status_change(struct work_struct *work) wf_otg); u32 count = 0; u32 gotgctl; + unsigned long flags; dev_dbg(hsotg->dev, "%s()\n", __func__); - gotgctl = readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); dev_dbg(hsotg->dev, "gotgctl=%0x\n", gotgctl); dev_dbg(hsotg->dev, "gotgctl.b.conidsts=%d\n", !!(gotgctl & GOTGCTL_CONID_B)); @@ -1381,8 +1384,10 @@ static void dwc2_conn_id_status_change(struct work_struct *work) hsotg->op_state = OTG_STATE_B_PERIPHERAL; dwc2_core_init(hsotg, false, -1); dwc2_enable_global_interrupts(hsotg); - s3c_hsotg_core_init_disconnected(hsotg, false); - s3c_hsotg_core_connect(hsotg); + spin_lock_irqsave(&hsotg->lock, flags); + dwc2_hsotg_core_init_disconnected(hsotg, false); + spin_unlock_irqrestore(&hsotg->lock, flags); + dwc2_hsotg_core_connect(hsotg); } else { /* A-Device connector (Host Mode) */ dev_dbg(hsotg->dev, "connId A\n"); @@ -1420,11 +1425,12 @@ static void dwc2_wakeup_detected(unsigned long data) hprt0 = dwc2_read_hprt0(hsotg); dev_dbg(hsotg->dev, "Resume: HPRT0=%0x\n", hprt0); hprt0 &= ~HPRT0_RES; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); dev_dbg(hsotg->dev, "Clear Resume: HPRT0=%0x\n", - readl(hsotg->regs + HPRT0)); + dwc2_readl(hsotg->regs + HPRT0)); dwc2_hcd_rem_wakeup(hsotg); + hsotg->bus_suspended = 0; /* Change to L0 state */ hsotg->lx_state = DWC2_L0; @@ -1450,30 +1456,35 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) spin_lock_irqsave(&hsotg->lock, flags); if (windex == hsotg->otg_port && dwc2_host_is_b_hnp_enabled(hsotg)) { - gotgctl = readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); gotgctl |= GOTGCTL_HSTSETHNPEN; - writel(gotgctl, hsotg->regs + GOTGCTL); + dwc2_writel(gotgctl, hsotg->regs + GOTGCTL); hsotg->op_state = OTG_STATE_A_SUSPEND; } hprt0 = dwc2_read_hprt0(hsotg); hprt0 |= HPRT0_SUSP; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); - /* Update lx_state */ - hsotg->lx_state = DWC2_L2; + hsotg->bus_suspended = 1; - /* Suspend the Phy Clock */ - pcgctl = readl(hsotg->regs + PCGCTL); - pcgctl |= PCGCTL_STOPPCLK; - writel(pcgctl, hsotg->regs + PCGCTL); - udelay(10); + /* + * If hibernation is supported, Phy clock will be suspended + * after registers are backuped. + */ + if (!hsotg->core_params->hibernation) { + /* Suspend the Phy Clock */ + pcgctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgctl |= PCGCTL_STOPPCLK; + dwc2_writel(pcgctl, hsotg->regs + PCGCTL); + udelay(10); + } /* For HNP the bus must be suspended for at least 200ms */ if (dwc2_host_is_b_hnp_enabled(hsotg)) { - pcgctl = readl(hsotg->regs + PCGCTL); + pcgctl = dwc2_readl(hsotg->regs + PCGCTL); pcgctl &= ~PCGCTL_STOPPCLK; - writel(pcgctl, hsotg->regs + PCGCTL); + dwc2_writel(pcgctl, hsotg->regs + PCGCTL); spin_unlock_irqrestore(&hsotg->lock, flags); @@ -1483,6 +1494,44 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) } } +/* Must NOT be called with interrupt disabled or spinlock held */ +static void dwc2_port_resume(struct dwc2_hsotg *hsotg) +{ + unsigned long flags; + u32 hprt0; + u32 pcgctl; + + spin_lock_irqsave(&hsotg->lock, flags); + + /* + * If hibernation is supported, Phy clock is already resumed + * after registers restore. + */ + if (!hsotg->core_params->hibernation) { + pcgctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgctl &= ~PCGCTL_STOPPCLK; + dwc2_writel(pcgctl, hsotg->regs + PCGCTL); + spin_unlock_irqrestore(&hsotg->lock, flags); + usleep_range(20000, 40000); + spin_lock_irqsave(&hsotg->lock, flags); + } + + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_RES; + hprt0 &= ~HPRT0_SUSP; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + spin_unlock_irqrestore(&hsotg->lock, flags); + + msleep(USB_RESUME_TIMEOUT); + + spin_lock_irqsave(&hsotg->lock, flags); + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 &= ~(HPRT0_RES | HPRT0_SUSP); + dwc2_writel(hprt0, hsotg->regs + HPRT0); + hsotg->bus_suspended = 0; + spin_unlock_irqrestore(&hsotg->lock, flags); +} + /* Handles hub class-specific requests */ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, u16 wvalue, u16 windex, char *buf, u16 wlength) @@ -1522,23 +1571,15 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "ClearPortFeature USB_PORT_FEAT_ENABLE\n"); hprt0 = dwc2_read_hprt0(hsotg); hprt0 |= HPRT0_ENA; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); break; case USB_PORT_FEAT_SUSPEND: dev_dbg(hsotg->dev, "ClearPortFeature USB_PORT_FEAT_SUSPEND\n"); - writel(0, hsotg->regs + PCGCTL); - usleep_range(20000, 40000); - - hprt0 = dwc2_read_hprt0(hsotg); - hprt0 |= HPRT0_RES; - writel(hprt0, hsotg->regs + HPRT0); - hprt0 &= ~HPRT0_SUSP; - msleep(USB_RESUME_TIMEOUT); - hprt0 &= ~HPRT0_RES; - writel(hprt0, hsotg->regs + HPRT0); + if (hsotg->bus_suspended) + dwc2_port_resume(hsotg); break; case USB_PORT_FEAT_POWER: @@ -1546,7 +1587,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "ClearPortFeature USB_PORT_FEAT_POWER\n"); hprt0 = dwc2_read_hprt0(hsotg); hprt0 &= ~HPRT0_PWR; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); break; case USB_PORT_FEAT_INDICATOR: @@ -1667,7 +1708,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, break; } - hprt0 = readl(hsotg->regs + HPRT0); + hprt0 = dwc2_readl(hsotg->regs + HPRT0); dev_vdbg(hsotg->dev, " HPRT0: 0x%08x\n", hprt0); if (hprt0 & HPRT0_CONNSTS) @@ -1732,18 +1773,18 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "SetPortFeature - USB_PORT_FEAT_POWER\n"); hprt0 = dwc2_read_hprt0(hsotg); hprt0 |= HPRT0_PWR; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); break; case USB_PORT_FEAT_RESET: hprt0 = dwc2_read_hprt0(hsotg); dev_dbg(hsotg->dev, "SetPortFeature - USB_PORT_FEAT_RESET\n"); - pcgctl = readl(hsotg->regs + PCGCTL); + pcgctl = dwc2_readl(hsotg->regs + PCGCTL); pcgctl &= ~(PCGCTL_ENBL_SLEEP_GATING | PCGCTL_STOPPCLK); - writel(pcgctl, hsotg->regs + PCGCTL); + dwc2_writel(pcgctl, hsotg->regs + PCGCTL); /* ??? Original driver does this */ - writel(0, hsotg->regs + PCGCTL); + dwc2_writel(0, hsotg->regs + PCGCTL); hprt0 = dwc2_read_hprt0(hsotg); /* Clear suspend bit if resetting from suspend state */ @@ -1758,13 +1799,13 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, hprt0 |= HPRT0_PWR | HPRT0_RST; dev_dbg(hsotg->dev, "In host mode, hprt0=%08x\n", hprt0); - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); } /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */ usleep_range(50000, 70000); hprt0 &= ~HPRT0_RST; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); hsotg->lx_state = DWC2_L0; /* Now back to On state */ break; @@ -1774,6 +1815,15 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, /* Not supported */ break; + case USB_PORT_FEAT_TEST: + hprt0 = dwc2_read_hprt0(hsotg); + dev_dbg(hsotg->dev, + "SetPortFeature - USB_PORT_FEAT_TEST\n"); + hprt0 &= ~HPRT0_TSTCTL_MASK; + hprt0 |= (windex >> 8) << HPRT0_TSTCTL_SHIFT; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + break; + default: retval = -EINVAL; dev_err(hsotg->dev, @@ -1828,7 +1878,7 @@ static int dwc2_hcd_is_status_changed(struct dwc2_hsotg *hsotg, int port) int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg) { - u32 hfnum = readl(hsotg->regs + HFNUM); + u32 hfnum = dwc2_readl(hsotg->regs + HFNUM); #ifdef DWC2_DEBUG_SOF dev_vdbg(hsotg->dev, "DWC OTG HCD GET FRAME NUMBER %d\n", @@ -1931,11 +1981,11 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg) if (chan->xfer_started) { u32 hfnum, hcchar, hctsiz, hcint, hcintmsk; - hfnum = readl(hsotg->regs + HFNUM); - hcchar = readl(hsotg->regs + HCCHAR(i)); - hctsiz = readl(hsotg->regs + HCTSIZ(i)); - hcint = readl(hsotg->regs + HCINT(i)); - hcintmsk = readl(hsotg->regs + HCINTMSK(i)); + hfnum = dwc2_readl(hsotg->regs + HFNUM); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); + hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(i)); + hcint = dwc2_readl(hsotg->regs + HCINT(i)); + hcintmsk = dwc2_readl(hsotg->regs + HCINTMSK(i)); dev_dbg(hsotg->dev, " hfnum: 0x%08x\n", hfnum); dev_dbg(hsotg->dev, " hcchar: 0x%08x\n", hcchar); dev_dbg(hsotg->dev, " hctsiz: 0x%08x\n", hctsiz); @@ -1983,12 +2033,12 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, " periodic_channels: %d\n", hsotg->periodic_channels); dev_dbg(hsotg->dev, " periodic_usecs: %d\n", hsotg->periodic_usecs); - np_tx_status = readl(hsotg->regs + GNPTXSTS); + np_tx_status = dwc2_readl(hsotg->regs + GNPTXSTS); dev_dbg(hsotg->dev, " NP Tx Req Queue Space Avail: %d\n", (np_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT); dev_dbg(hsotg->dev, " NP Tx FIFO Space Avail: %d\n", (np_tx_status & TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT); - p_tx_status = readl(hsotg->regs + HPTXSTS); + p_tx_status = dwc2_readl(hsotg->regs + HPTXSTS); dev_dbg(hsotg->dev, " P Tx Req Queue Space Avail: %d\n", (p_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT); dev_dbg(hsotg->dev, " P Tx FIFO Space Avail: %d\n", @@ -2184,11 +2234,6 @@ void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, usb_pipein(urb->pipe) ? "IN" : "OUT", status, urb->actual_length); - if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS && dbg_perio()) { - for (i = 0; i < urb->number_of_packets; i++) - dev_vdbg(hsotg->dev, " ISO Desc %d status %d\n", - i, urb->iso_frame_desc[i].status); - } if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { urb->error_count = dwc2_hcd_urb_get_error_count(qtd->urb); @@ -2201,6 +2246,12 @@ void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, } } + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS && dbg_perio()) { + for (i = 0; i < urb->number_of_packets; i++) + dev_vdbg(hsotg->dev, " ISO Desc %d status %d\n", + i, urb->iso_frame_desc[i].status); + } + urb->status = status; if (!status) { if ((urb->transfer_flags & URB_SHORT_NOT_OK) && @@ -2252,7 +2303,7 @@ static void dwc2_hcd_reset_func(struct work_struct *work) dev_dbg(hsotg->dev, "USB RESET function called\n"); hprt0 = dwc2_read_hprt0(hsotg); hprt0 &= ~HPRT0_RST; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); hsotg->flags.b.port_reset_change = 1; } @@ -2276,8 +2327,9 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd) dev_dbg(hsotg->dev, "DWC OTG HCD START\n"); spin_lock_irqsave(&hsotg->lock, flags); - + hsotg->lx_state = DWC2_L0; hcd->state = HC_STATE_RUNNING; + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); if (dwc2_is_device_mode(hsotg)) { spin_unlock_irqrestore(&hsotg->lock, flags); @@ -2306,13 +2358,148 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd) struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); unsigned long flags; + /* Turn off all host-specific interrupts */ + dwc2_disable_host_interrupts(hsotg); + + /* Wait for interrupt processing to finish */ + synchronize_irq(hcd->irq); + spin_lock_irqsave(&hsotg->lock, flags); + /* Ensure hcd is disconnected */ + dwc2_hcd_disconnect(hsotg); dwc2_hcd_stop(hsotg); + hsotg->lx_state = DWC2_L3; + hcd->state = HC_STATE_HALT; + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); spin_unlock_irqrestore(&hsotg->lock, flags); usleep_range(1000, 3000); } +static int _dwc2_hcd_suspend(struct usb_hcd *hcd) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + unsigned long flags; + int ret = 0; + u32 hprt0; + + spin_lock_irqsave(&hsotg->lock, flags); + + if (hsotg->lx_state != DWC2_L0) + goto unlock; + + if (!HCD_HW_ACCESSIBLE(hcd)) + goto unlock; + + if (!hsotg->core_params->hibernation) + goto skip_power_saving; + + /* + * Drive USB suspend and disable port Power + * if usb bus is not suspended. + */ + if (!hsotg->bus_suspended) { + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_SUSP; + hprt0 &= ~HPRT0_PWR; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + } + + /* Enter hibernation */ + ret = dwc2_enter_hibernation(hsotg); + if (ret) { + if (ret != -ENOTSUPP) + dev_err(hsotg->dev, + "enter hibernation failed\n"); + goto skip_power_saving; + } + + /* Ask phy to be suspended */ + if (!IS_ERR_OR_NULL(hsotg->uphy)) { + spin_unlock_irqrestore(&hsotg->lock, flags); + usb_phy_set_suspend(hsotg->uphy, true); + spin_lock_irqsave(&hsotg->lock, flags); + } + + /* After entering hibernation, hardware is no more accessible */ + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + +skip_power_saving: + hsotg->lx_state = DWC2_L2; +unlock: + spin_unlock_irqrestore(&hsotg->lock, flags); + + return ret; +} + +static int _dwc2_hcd_resume(struct usb_hcd *hcd) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&hsotg->lock, flags); + + if (hsotg->lx_state != DWC2_L2) + goto unlock; + + if (!hsotg->core_params->hibernation) { + hsotg->lx_state = DWC2_L0; + goto unlock; + } + + /* + * Set HW accessible bit before powering on the controller + * since an interrupt may rise. + */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + /* + * Enable power if not already done. + * This must not be spinlocked since duration + * of this call is unknown. + */ + if (!IS_ERR_OR_NULL(hsotg->uphy)) { + spin_unlock_irqrestore(&hsotg->lock, flags); + usb_phy_set_suspend(hsotg->uphy, false); + spin_lock_irqsave(&hsotg->lock, flags); + } + + /* Exit hibernation */ + ret = dwc2_exit_hibernation(hsotg, true); + if (ret && (ret != -ENOTSUPP)) + dev_err(hsotg->dev, "exit hibernation failed\n"); + + hsotg->lx_state = DWC2_L0; + + spin_unlock_irqrestore(&hsotg->lock, flags); + + if (hsotg->bus_suspended) { + spin_lock_irqsave(&hsotg->lock, flags); + hsotg->flags.b.port_suspend_change = 1; + spin_unlock_irqrestore(&hsotg->lock, flags); + dwc2_port_resume(hsotg); + } else { + /* Wait for controller to correctly update D+/D- level */ + usleep_range(3000, 5000); + + /* + * Clear Port Enable and Port Status changes. + * Enable Port Power. + */ + dwc2_writel(HPRT0_PWR | HPRT0_CONNDET | + HPRT0_ENACHG, hsotg->regs + HPRT0); + /* Wait for controller to detect Port Connect */ + usleep_range(5000, 7000); + } + + return ret; +unlock: + spin_unlock_irqrestore(&hsotg->lock, flags); + + return ret; +} + /* Returns the current frame number */ static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd) { @@ -2415,6 +2602,9 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, u32 tflags = 0; void *buf; unsigned long flags; + struct dwc2_qh *qh; + bool qh_allocated = false; + struct dwc2_qtd *qtd; if (dbg_urb(urb)) { dev_vdbg(hsotg->dev, "DWC OTG HCD URB Enqueue\n"); @@ -2468,7 +2658,7 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, "%s: unaligned transfer with no transfer_buffer", __func__); retval = -EINVAL; - goto fail1; + goto fail0; } } @@ -2493,34 +2683,63 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, urb->iso_frame_desc[i].length); urb->hcpriv = dwc2_urb; + qh = (struct dwc2_qh *) ep->hcpriv; + /* Create QH for the endpoint if it doesn't exist */ + if (!qh) { + qh = dwc2_hcd_qh_create(hsotg, dwc2_urb, mem_flags); + if (!qh) { + retval = -ENOMEM; + goto fail0; + } + ep->hcpriv = qh; + qh_allocated = true; + } + + qtd = kzalloc(sizeof(*qtd), mem_flags); + if (!qtd) { + retval = -ENOMEM; + goto fail1; + } spin_lock_irqsave(&hsotg->lock, flags); retval = usb_hcd_link_urb_to_ep(hcd, urb); - spin_unlock_irqrestore(&hsotg->lock, flags); if (retval) - goto fail1; + goto fail2; - retval = dwc2_hcd_urb_enqueue(hsotg, dwc2_urb, &ep->hcpriv, mem_flags); + retval = dwc2_hcd_urb_enqueue(hsotg, dwc2_urb, qh, qtd); if (retval) - goto fail2; + goto fail3; if (alloc_bandwidth) { - spin_lock_irqsave(&hsotg->lock, flags); dwc2_allocate_bus_bandwidth(hcd, dwc2_hcd_get_ep_bandwidth(hsotg, ep), urb); - spin_unlock_irqrestore(&hsotg->lock, flags); } + spin_unlock_irqrestore(&hsotg->lock, flags); + return 0; -fail2: - spin_lock_irqsave(&hsotg->lock, flags); +fail3: dwc2_urb->priv = NULL; usb_hcd_unlink_urb_from_ep(hcd, urb); +fail2: spin_unlock_irqrestore(&hsotg->lock, flags); -fail1: urb->hcpriv = NULL; + kfree(qtd); +fail1: + if (qh_allocated) { + struct dwc2_qtd *qtd2, *qtd2_tmp; + + ep->hcpriv = NULL; + dwc2_hcd_qh_unlink(hsotg, qh); + /* Free each QTD in the QH's QTD list */ + list_for_each_entry_safe(qtd2, qtd2_tmp, &qh->qtd_list, + qtd_list_entry) + dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh); + dwc2_hcd_qh_free(hsotg, qh); + } +fail0: kfree(dwc2_urb); return retval; @@ -2683,6 +2902,9 @@ static struct hc_driver dwc2_hc_driver = { .hub_status_data = _dwc2_hcd_hub_status_data, .hub_control = _dwc2_hcd_hub_control, .clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete, + + .bus_suspend = _dwc2_hcd_suspend, + .bus_resume = _dwc2_hcd_resume, }; /* @@ -2729,17 +2951,17 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg) hsotg->status_buf = NULL; } - ahbcfg = readl(hsotg->regs + GAHBCFG); + ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG); /* Disable all interrupts */ ahbcfg &= ~GAHBCFG_GLBL_INTR_EN; - writel(ahbcfg, hsotg->regs + GAHBCFG); - writel(0, hsotg->regs + GINTMSK); + dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG); + dwc2_writel(0, hsotg->regs + GINTMSK); if (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a) { - dctl = readl(hsotg->regs + DCTL); + dctl = dwc2_readl(hsotg->regs + DCTL); dctl |= DCTL_SFTDISCON; - writel(dctl, hsotg->regs + DCTL); + dwc2_writel(dctl, hsotg->regs + DCTL); } if (hsotg->wq_otg) { @@ -2748,8 +2970,6 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg) destroy_workqueue(hsotg->wq_otg); } - kfree(hsotg->core_params); - hsotg->core_params = NULL; del_timer(&hsotg->wkp_timer); } @@ -2762,29 +2982,12 @@ static void dwc2_hcd_release(struct dwc2_hsotg *hsotg) } /* - * Sets all parameters to the given value. - * - * Assumes that the dwc2_core_params struct contains only integers. - */ -void dwc2_set_all_params(struct dwc2_core_params *params, int value) -{ - int *p = (int *)params; - size_t size = sizeof(*params) / sizeof(*p); - int i; - - for (i = 0; i < size; i++) - p[i] = value; -} -EXPORT_SYMBOL_GPL(dwc2_set_all_params); - -/* * Initializes the HCD. This function allocates memory for and initializes the * static parts of the usb_hcd and dwc2_hsotg structures. It also registers the * USB bus with the core and calls the hc_driver->start() function. It returns * a negative error on failure. */ -int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, - const struct dwc2_core_params *params) +int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq) { struct usb_hcd *hcd; struct dwc2_host_chan *channel; @@ -2797,15 +3000,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, dev_dbg(hsotg->dev, "DWC OTG HCD INIT\n"); - /* Detect config values from hardware */ - retval = dwc2_get_hwparams(hsotg); - - if (retval) - return retval; - retval = -ENOMEM; - hcfg = readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg->regs + HCFG); dev_dbg(hsotg->dev, "hcfg=%08x\n", hcfg); #ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS @@ -2821,15 +3018,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, hsotg->last_frame_num = HFNUM_MAX_FRNUM; #endif - hsotg->core_params = kzalloc(sizeof(*hsotg->core_params), GFP_KERNEL); - if (!hsotg->core_params) - goto error1; - - dwc2_set_all_params(hsotg->core_params, -1); - - /* Validate parameter values */ - dwc2_set_parameters(hsotg, params); - /* Check if the bus driver or platform code has setup a dma_mask */ if (hsotg->core_params->dma_enable > 0 && hsotg->dev->dma_mask == NULL) { @@ -2947,6 +3135,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, /* Don't support SG list at this point */ hcd->self.sg_tablesize = 0; + if (!IS_ERR_OR_NULL(hsotg->uphy)) + otg_set_host(hsotg->uphy->otg, &hcd->self); + /* * Finish generic HCD initialization and start the HCD. This function * allocates the DMA buffer pool, registers the USB bus, requests the @@ -2979,7 +3170,6 @@ error1: dev_err(hsotg->dev, "%s() FAILED, returning %d\n", __func__, retval); return retval; } -EXPORT_SYMBOL_GPL(dwc2_hcd_init); /* * Removes the HCD. @@ -3000,6 +3190,9 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) return; } + if (!IS_ERR_OR_NULL(hsotg->uphy)) + otg_set_host(hsotg->uphy->otg, NULL); + usb_remove_hcd(hcd); hsotg->priv = NULL; dwc2_hcd_release(hsotg); @@ -3010,4 +3203,3 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) kfree(hsotg->frame_num_array); #endif } -EXPORT_SYMBOL_GPL(dwc2_hcd_remove); |