static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { enum usb_device_state state = dwc->gadget.state; u32 addr; u32 reg; addr = le16_to_cpu(ctrl->wValue); if (addr > 127) { dwc3_trace(trace_dwc3_ep0, "invalid device address %d", addr); return -EINVAL; } if (state == USB_STATE_CONFIGURED) { dwc3_trace(trace_dwc3_ep0, "trying to set address when configured"); return -EINVAL; } reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg &= ~(DWC3_DCFG_DEVADDR_MASK); reg |= DWC3_DCFG_DEVADDR(addr); dwc3_writel(dwc->regs, DWC3_DCFG, reg); if (addr) usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS); else usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT); return 0; }
static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; dep->flags &= ~DWC3_EP_BUSY; dep->resource_index = 0; dwc->setup_packet_pending = false; switch (dwc->ep0state) { case EP0_SETUP_PHASE: dwc3_trace(trace_dwc3_ep0, "Setup Phase"); dwc3_ep0_inspect_setup(dwc, event); break; case EP0_DATA_PHASE: dwc3_trace(trace_dwc3_ep0, "Data Phase"); dwc3_ep0_complete_data(dwc, event); break; case EP0_STATUS_PHASE: dwc3_trace(trace_dwc3_ep0, "Status Phase"); dwc3_ep0_complete_status(dwc, event); break; default: WARN(true, "UNKNOWN ep0state %d\n", dwc->ep0state); } }
static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, u32 len, u32 type, bool chain) { struct dwc3_gadget_ep_cmd_params params; struct dwc3_trb *trb; struct dwc3_ep *dep; int ret; dep = dwc->eps[epnum]; if (dep->flags & DWC3_EP_BUSY) { dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name); return 0; } trb = &dwc->ep0_trb[dep->free_slot]; if (chain) dep->free_slot++; trb->bpl = lower_32_bits(buf_dma); trb->bph = upper_32_bits(buf_dma); trb->size = len; trb->ctrl = type; trb->ctrl |= (DWC3_TRB_CTRL_HWO | DWC3_TRB_CTRL_ISP_IMI); if (chain) trb->ctrl |= DWC3_TRB_CTRL_CHN; else trb->ctrl |= (DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_LST); if (chain) return 0; memset(¶ms, 0, sizeof(params)); params.param0 = upper_32_bits(dwc->ep0_trb_addr); params.param1 = lower_32_bits(dwc->ep0_trb_addr); trace_dwc3_prepare_trb(dep, trb); ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_STARTTRANSFER, ¶ms); if (ret < 0) { dwc3_trace(trace_dwc3_ep0, "%s STARTTRANSFER failed", dep->name); return ret; } dep->flags |= DWC3_EP_BUSY; dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc, dep->number); dwc->ep0_next_event = DWC3_EP0_COMPLETE; return 0; }
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, struct dwc3_ep *dep, struct dwc3_request *req) { int ret; req->direction = !!dep->number; if (req->request.length == 0) { ret = dwc3_ep0_start_trans(dwc, dep->number, dwc->ctrl_req_addr, 0, DWC3_TRBCTL_CONTROL_DATA, false); } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) && (dep->number == 0)) { u32 transfer_size = 0; u32 maxpacket; ret = usb_gadget_map_request(&dwc->gadget, &req->request, dep->number); if (ret) { dwc3_trace(trace_dwc3_ep0, "failed to map request\n"); return; } maxpacket = dep->endpoint.maxpacket; if (req->request.length > DWC3_EP0_BOUNCE_SIZE) { transfer_size = ALIGN(req->request.length - maxpacket, maxpacket); ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma, transfer_size, DWC3_TRBCTL_CONTROL_DATA, true); } transfer_size = roundup((req->request.length - transfer_size), maxpacket); dwc->ep0_bounced = true; ret = dwc3_ep0_start_trans(dwc, dep->number, dwc->ep0_bounce_addr, transfer_size, DWC3_TRBCTL_CONTROL_DATA, false); } else { ret = usb_gadget_map_request(&dwc->gadget, &req->request, dep->number); if (ret) { dwc3_trace(trace_dwc3_ep0, "failed to map request\n"); return; } ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma, req->request.length, DWC3_TRBCTL_CONTROL_DATA, false); } WARN_ON(ret < 0); }
/** * dwc3_event_buffers_setup - setup our allocated event buffers * @dwc: pointer to our controller context structure * * Returns 0 on success otherwise negative errno. */ static int dwc3_event_buffers_setup(struct dwc3 *dwc) { struct dwc3_event_buffer *evt; int n; for (n = 0; n < dwc->num_event_buffers; n++) { evt = dwc->ev_buffs[n]; dwc3_trace(trace_dwc3_core, "Event buf %p dma %08llx length %d\n", evt->buf, (unsigned long long) evt->dma, evt->length); evt->lpos = 0; dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), lower_32_bits(evt->dma)); dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), upper_32_bits(evt->dma)); dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), DWC3_GEVNTSIZ_SIZE(evt->length)); dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); } return 0; }
static void dwc3_ep0_xfernotready(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { dwc->setup_packet_pending = true; switch (event->status) { case DEPEVT_STATUS_CONTROL_DATA: dwc3_trace(trace_dwc3_ep0, "Control Data"); /* * We already have a DATA transfer in the controller's cache, * if we receive a XferNotReady(DATA) we will ignore it, unless * it's for the wrong direction. * * In that case, we must issue END_TRANSFER command to the Data * Phase we already have started and issue SetStall on the * control endpoint. */ if (dwc->ep0_expect_in != event->endpoint_number) { struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in]; dwc3_trace(trace_dwc3_ep0, "Wrong direction for Data phase"); dwc3_ep0_end_control_data(dwc, dep); dwc3_ep0_stall_and_restart(dwc); return; } break; case DEPEVT_STATUS_CONTROL_STATUS: if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) return; dwc3_trace(trace_dwc3_ep0, "Control Status"); dwc->ep0state = EP0_STATUS_PHASE; if (dwc->delayed_status) { WARN_ON_ONCE(event->endpoint_number != 1); dwc3_trace(trace_dwc3_ep0, "Delayed Status"); return; } dwc3_ep0_do_control_status(dwc, event); } }
static void dwc3_core_num_eps(struct dwc3 *dwc) { struct dwc3_hwparams *parms = &dwc->hwparams; dwc->num_in_eps = DWC3_NUM_IN_EPS(parms); dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps; dwc3_trace(trace_dwc3_core, "found %d IN and %d OUT endpoints", dwc->num_in_eps, dwc->num_out_eps); }
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep) { if (dwc->resize_fifos) { dwc3_trace(trace_dwc3_ep0, "Resizing FIFOs"); dwc3_gadget_resize_tx_fifos(dwc); dwc->resize_fifos = 0; } WARN_ON(dwc3_ep0_start_control_status(dep)); }
static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { enum usb_device_state state = dwc->gadget.state; u32 cfg; int ret; u32 reg; dwc->start_config_issued = false; cfg = le16_to_cpu(ctrl->wValue); switch (state) { case USB_STATE_DEFAULT: return -EINVAL; break; case USB_STATE_ADDRESS: ret = dwc3_ep0_delegate_req(dwc, ctrl); /* if the cfg matches and the cfg is non zero */ if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) { /* * only change state if set_config has already * been processed. If gadget driver returns * USB_GADGET_DELAYED_STATUS, we will wait * to change the state on the next usb_ep_queue() */ if (ret == 0) usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED); /* * Enable transition to U1/U2 state when * nothing is pending from application. */ reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA); dwc3_writel(dwc->regs, DWC3_DCTL, reg); dwc->resize_fifos = true; dwc3_trace(trace_dwc3_ep0, "resize FIFOs flag SET"); } break; case USB_STATE_CONFIGURED: ret = dwc3_ep0_delegate_req(dwc, ctrl); if (!cfg && !ret) usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS); break; default: ret = -EINVAL; } return ret; }
static void dwc3_ep0_complete_status(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { struct dwc3_request *r; struct dwc3_ep *dep; struct dwc3_trb *trb; u32 status; dep = dwc->eps[0]; trb = dwc->ep0_trb; trace_dwc3_complete_trb(dep, trb); if (!list_empty(&dep->request_list)) { r = next_request(&dep->request_list); dwc3_gadget_giveback(dep, r, 0); } if (dwc->test_mode) { int ret; ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr); if (ret < 0) { dwc3_trace(trace_dwc3_ep0, "Invalid Test #%d", dwc->test_mode_nr); dwc3_ep0_stall_and_restart(dwc); return; } } status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (status == DWC3_TRBSTS_SETUP_PENDING) { dwc->setup_packet_pending = true; dwc3_trace(trace_dwc3_ep0, "Setup Pending received"); } dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); }
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, gfp_t gfp_flags) { struct dwc3_request *req = to_dwc3_request(request); struct dwc3_ep *dep = to_dwc3_ep(ep); struct dwc3 *dwc = dep->dwc; unsigned long flags; int ret; spin_lock_irqsave(&dwc->lock, flags); if (!dep->endpoint.desc) { dwc3_trace(trace_dwc3_ep0, "trying to queue request %p to disabled %s", request, dep->name); ret = -ESHUTDOWN; goto out; } /* we share one TRB for ep0/1 */ if (!list_empty(&dep->request_list)) { ret = -EBUSY; goto out; } dwc3_trace(trace_dwc3_ep0, "queueing request %p to %s length %d state '%s'", request, dep->name, request->length, dwc3_ep0_state_string(dwc->ep0state)); ret = __dwc3_gadget_ep0_queue(dep, req); out: spin_unlock_irqrestore(&dwc->lock, flags); return ret; }
static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { int ret; switch (ctrl->bRequest) { case USB_REQ_GET_STATUS: dwc3_trace(trace_dwc3_ep0, "USB_REQ_GET_STATUS\n"); ret = dwc3_ep0_handle_status(dwc, ctrl); break; case USB_REQ_CLEAR_FEATURE: dwc3_trace(trace_dwc3_ep0, "USB_REQ_CLEAR_FEATURE\n"); ret = dwc3_ep0_handle_feature(dwc, ctrl, 0); break; case USB_REQ_SET_FEATURE: dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_FEATURE\n"); ret = dwc3_ep0_handle_feature(dwc, ctrl, 1); break; case USB_REQ_SET_ADDRESS: dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ADDRESS\n"); ret = dwc3_ep0_set_address(dwc, ctrl); break; case USB_REQ_SET_CONFIGURATION: dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_CONFIGURATION\n"); ret = dwc3_ep0_set_config(dwc, ctrl); break; case USB_REQ_SET_SEL: dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_SEL\n"); ret = dwc3_ep0_set_sel(dwc, ctrl); break; case USB_REQ_SET_ISOCH_DELAY: dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY\n"); ret = dwc3_ep0_set_isoch_delay(dwc, ctrl); break; case USB_REQ_SET_INTERFACE: dev_vdbg(dwc->dev, "USB_REQ_SET_INTERFACE\n"); dwc->start_config_issued = false; /* Fall through */ default: dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver\n"); ret = dwc3_ep0_delegate_req(dwc, ctrl); break; } return ret; }
static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { int ret; switch (ctrl->bRequest) { case USB_REQ_GET_STATUS: dwc3_trace(trace_dwc3_ep0, "USB_REQ_GET_STATUS"); ret = dwc3_ep0_handle_status(dwc, ctrl); break; case USB_REQ_CLEAR_FEATURE: dwc3_trace(trace_dwc3_ep0, "USB_REQ_CLEAR_FEATURE"); ret = dwc3_ep0_handle_feature(dwc, ctrl, 0); break; case USB_REQ_SET_FEATURE: dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_FEATURE"); ret = dwc3_ep0_handle_feature(dwc, ctrl, 1); break; case USB_REQ_SET_ADDRESS: dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ADDRESS"); ret = dwc3_ep0_set_address(dwc, ctrl); break; case USB_REQ_SET_CONFIGURATION: dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_CONFIGURATION"); ret = dwc3_ep0_set_config(dwc, ctrl); break; case USB_REQ_SET_SEL: dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_SEL"); ret = dwc3_ep0_set_sel(dwc, ctrl); break; case USB_REQ_SET_ISOCH_DELAY: dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY"); ret = dwc3_ep0_set_isoch_delay(dwc, ctrl); break; default: dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver"); ret = dwc3_ep0_delegate_req(dwc, ctrl); break; } return ret; }
static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, struct dwc3_request *req) { struct dwc3 *dwc = dep->dwc; req->request.actual = 0; req->request.status = -EINPROGRESS; req->epnum = dep->number; list_add_tail(&req->list, &dep->request_list); /* * Gadget driver might not be quick enough to queue a request * before we get a Transfer Not Ready event on this endpoint. * * In that case, we will set DWC3_EP_PENDING_REQUEST. When that * flag is set, it's telling us that as soon as Gadget queues the * required request, we should kick the transfer here because the * IRQ we were waiting for is long gone. */ if (dep->flags & DWC3_EP_PENDING_REQUEST) { unsigned direction; direction = !!(dep->flags & DWC3_EP0_DIR_IN); if (dwc->ep0state != EP0_DATA_PHASE) { dev_WARN(dwc->dev, "Unexpected pending request\n"); return 0; } __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req); dep->flags &= ~(DWC3_EP_PENDING_REQUEST | DWC3_EP0_DIR_IN); return 0; } /* * In case gadget driver asked us to delay the STATUS phase, * handle it here. */ if (dwc->delayed_status) { unsigned direction; direction = !dwc->ep0_expect_in; dwc->delayed_status = false; usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED); if (dwc->ep0state == EP0_STATUS_PHASE) __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); else dwc3_trace(trace_dwc3_ep0, "too early for delayed status"); return 0; } /* * Unfortunately we have uncovered a limitation wrt the Data Phase. * * Section 9.4 says we can wait for the XferNotReady(DATA) event to * come before issueing Start Transfer command, but if we do, we will * miss situations where the host starts another SETUP phase instead of * the DATA phase. Such cases happen at least on TD.7.6 of the Link * Layer Compliance Suite. * * The problem surfaces due to the fact that in case of back-to-back * SETUP packets there will be no XferNotReady(DATA) generated and we * will be stuck waiting for XferNotReady(DATA) forever. * * By looking at tables 9-13 and 9-14 of the Databook, we can see that * it tells us to start Data Phase right away. It also mentions that if * we receive a SETUP phase instead of the DATA phase, core will issue * XferComplete for the DATA phase, before actually initiating it in * the wire, with the TRB's status set to "SETUP_PENDING". Such status * can only be used to print some debugging logs, as the core expects * us to go through to the STATUS phase and start a CONTROL_STATUS TRB, * just so it completes right away, without transferring anything and, * only then, we can go back to the SETUP phase. * * Because of this scenario, SNPS decided to change the programming * model of control transfers and support on-demand transfers only for * the STATUS phase. To fix the issue we have now, we will always wait * for gadget driver to queue the DATA phase's struct usb_request, then * start it right away. * * If we're actually in a 2-stage transfer, we will wait for * XferNotReady(STATUS). */ if (dwc->three_stage_setup) { unsigned direction; direction = dwc->ep0_expect_in; dwc->ep0state = EP0_DATA_PHASE; __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req); dep->flags &= ~DWC3_EP0_DIR_IN; } return 0; }
static void dwc3_ep0_complete_data(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { struct dwc3_request *r = NULL; struct usb_request *ur; struct dwc3_trb *trb; struct dwc3_ep *ep0; unsigned transfer_size = 0; unsigned maxp; unsigned remaining_ur_length; void *buf; u32 transferred = 0; u32 status; u32 length; u8 epnum; epnum = event->endpoint_number; ep0 = dwc->eps[0]; dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; trb = dwc->ep0_trb; trace_dwc3_complete_trb(ep0, trb); r = next_request(&ep0->request_list); if (!r) return; status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (status == DWC3_TRBSTS_SETUP_PENDING) { dwc->setup_packet_pending = true; dwc3_trace(trace_dwc3_ep0, "Setup Pending received"); if (r) dwc3_gadget_giveback(ep0, r, -ECONNRESET); return; } ur = &r->request; buf = ur->buf; remaining_ur_length = ur->length; length = trb->size & DWC3_TRB_SIZE_MASK; maxp = ep0->endpoint.maxpacket; if (dwc->ep0_bounced) { /* * Handle the first TRB before handling the bounce buffer if * the request length is greater than the bounce buffer size */ if (ur->length > DWC3_EP0_BOUNCE_SIZE) { transfer_size = ALIGN(ur->length - maxp, maxp); transferred = transfer_size - length; buf = (u8 *)buf + transferred; ur->actual += transferred; remaining_ur_length -= transferred; trb++; length = trb->size & DWC3_TRB_SIZE_MASK; ep0->free_slot = 0; } transfer_size = roundup((ur->length - transfer_size), maxp); transferred = min_t(u32, remaining_ur_length, transfer_size - length); memcpy(buf, dwc->ep0_bounce, transferred); } else { transferred = ur->length - length; } ur->actual += transferred; if ((epnum & 1) && ur->actual < ur->length) { /* for some reason we did not get everything out */ dwc3_ep0_stall_and_restart(dwc); } else { dwc3_gadget_giveback(ep0, r, 0); if (IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) && ur->length && ur->zero) { int ret; dwc->ep0_next_event = DWC3_EP0_COMPLETE; ret = dwc3_ep0_start_trans(dwc, epnum, dwc->ctrl_req_addr, 0, DWC3_TRBCTL_CONTROL_DATA, false); WARN_ON(ret < 0); } } }
static void dwc3_ep0_complete_data(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { struct dwc3_request *r = NULL; struct usb_request *ur; struct dwc3_trb *trb; struct dwc3_ep *ep0; u32 transferred; u32 status; u32 length; u8 epnum; epnum = event->endpoint_number; ep0 = dwc->eps[0]; dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; trb = dwc->ep0_trb; r = next_request(&ep0->request_list); if (!r) return; status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (status == DWC3_TRBSTS_SETUP_PENDING) { dwc3_trace(trace_dwc3_ep0, "Setup Pending received"); if (r) dwc3_gadget_giveback(ep0, r, -ECONNRESET); return; } ur = &r->request; length = trb->size & DWC3_TRB_SIZE_MASK; if (dwc->ep0_bounced) { unsigned transfer_size = ur->length; unsigned maxp = ep0->endpoint.maxpacket; transfer_size += (maxp - (transfer_size % maxp)); transferred = min_t(u32, ur->length, transfer_size - length); memcpy(ur->buf, dwc->ep0_bounce, transferred); } else { transferred = ur->length - length; } ur->actual += transferred; if ((epnum & 1) && ur->actual < ur->length) { /* for some reason we did not get everything out */ dwc3_ep0_stall_and_restart(dwc); } else { dwc3_gadget_giveback(ep0, r, 0); if (IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) && ur->length && ur->zero) { int ret; dwc->ep0_next_event = DWC3_EP0_COMPLETE; ret = dwc3_ep0_start_trans(dwc, epnum, dwc->ctrl_req_addr, 0, DWC3_TRBCTL_CONTROL_DATA); WARN_ON(ret < 0); } } }
/** * dwc3_core_init - Low-level initialization of DWC3 Core * @dwc: Pointer to our controller context structure * * Returns 0 on success otherwise negative errno. */ static int dwc3_core_init(struct dwc3 *dwc) { u32 hwparams4 = dwc->hwparams.hwparams4; u32 reg; int ret; reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); /* This should read as U3 followed by revision number */ if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) { /* Detected DWC_usb3 IP */ dwc->revision = reg; } else if ((reg & DWC3_GSNPSID_MASK) == 0x33310000) { /* Detected DWC_usb31 IP */ dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER); dwc->revision |= DWC3_REVISION_IS_DWC31; } else { dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); ret = -ENODEV; goto err0; } /* * Write Linux Version Code to our GUID register so it's easy to figure * out which kernel version a bug was found. */ dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE); /* Handle USB2.0-only core configuration */ if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == DWC3_GHWPARAMS3_SSPHY_IFC_DIS) { if (dwc->maximum_speed == USB_SPEED_SUPER) dwc->maximum_speed = USB_SPEED_HIGH; } /* issue device SoftReset too */ ret = dwc3_soft_reset(dwc); if (ret) goto err0; ret = dwc3_core_soft_reset(dwc); if (ret) goto err0; reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg &= ~DWC3_GCTL_SCALEDOWN_MASK; switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) { case DWC3_GHWPARAMS1_EN_PWROPT_CLK: /** * WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an * issue which would cause xHCI compliance tests to fail. * * Because of that we cannot enable clock gating on such * configurations. * * Refers to: * * STAR#9000588375: Clock Gating, SOF Issues when ref_clk-Based * SOF/ITP Mode Used */ if ((dwc->dr_mode == USB_DR_MODE_HOST || dwc->dr_mode == USB_DR_MODE_OTG) && (dwc->revision >= DWC3_REVISION_210A && dwc->revision <= DWC3_REVISION_250A)) reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC; else reg &= ~DWC3_GCTL_DSBLCLKGTNG; break; case DWC3_GHWPARAMS1_EN_PWROPT_HIB: /* enable hibernation here */ dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4); /* * REVISIT Enabling this bit so that host-mode hibernation * will work. Device-mode hibernation is not yet implemented. */ reg |= DWC3_GCTL_GBLHIBERNATIONEN; break; default: dwc3_trace(trace_dwc3_core, "No power optimization available\n"); } /* check if current dwc3 is on simulation board */ if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) { dwc3_trace(trace_dwc3_core, "running on FPGA platform\n"); dwc->is_fpga = true; } WARN_ONCE(dwc->disable_scramble_quirk && !dwc->is_fpga, "disable_scramble cannot be used on non-FPGA builds\n"); if (dwc->disable_scramble_quirk && dwc->is_fpga) reg |= DWC3_GCTL_DISSCRAMBLE; else reg &= ~DWC3_GCTL_DISSCRAMBLE; if (dwc->u2exit_lfps_quirk) reg |= DWC3_GCTL_U2EXIT_LFPS; /* * WORKAROUND: DWC3 revisions <1.90a have a bug * where the device can fail to connect at SuperSpeed * and falls back to high-speed mode which causes * the device to enter a Connect/Disconnect loop */ if (dwc->revision < DWC3_REVISION_190A) reg |= DWC3_GCTL_U2RSTECN; dwc3_core_num_eps(dwc); dwc3_writel(dwc->regs, DWC3_GCTL, reg); ret = dwc3_alloc_scratch_buffers(dwc); if (ret) goto err1; ret = dwc3_setup_scratch_buffers(dwc); if (ret) goto err2; return 0; err2: dwc3_free_scratch_buffers(dwc); err1: usb_phy_shutdown(dwc->usb2_phy); usb_phy_shutdown(dwc->usb3_phy); phy_exit(dwc->usb2_generic_phy); phy_exit(dwc->usb3_generic_phy); err0: return ret; }