int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) { struct dwc3_ep *dep = to_dwc3_ep(ep); struct dwc3 *dwc = dep->dwc; dbg_event(dep->number, "EP0STAL", value); dwc3_ep0_stall_and_restart(dwc); return 0; }
/** * dwc3_otg_init - Initializes otg related registers * @dwc: Pointer to out controller context structure * * Returns 0 on success otherwise negative errno. */ int dwc3_otg_init(struct dwc3 *dwc) { struct dwc3_otg *dotg; dev_dbg(dwc->dev, "dwc3_otg_init\n"); /* Allocate and init otg instance */ dotg = devm_kzalloc(dwc->dev, sizeof(struct dwc3_otg), GFP_KERNEL); if (!dotg) { dev_err(dwc->dev, "unable to allocate dwc3_otg\n"); return -ENOMEM; } dotg->otg.phy = devm_kzalloc(dwc->dev, sizeof(struct usb_phy), GFP_KERNEL); if (!dotg->otg.phy) { dev_err(dwc->dev, "unable to allocate dwc3_otg.phy\n"); return -ENOMEM; } dotg->otg.phy->otg = &dotg->otg; dotg->otg.phy->dev = dwc->dev; dotg->otg.phy->set_power = dwc3_otg_set_power; dotg->otg.set_peripheral = dwc3_otg_set_peripheral; dotg->otg.phy->set_suspend = dwc3_otg_set_suspend; dotg->otg.phy->state = OTG_STATE_UNDEFINED; dotg->regs = dwc->regs; /* This reference is used by dwc3 modules for checking otg existance */ dwc->dotg = dotg; dotg->dwc = dwc; dotg->otg.phy->dev = dwc->dev; wake_lock_init(&dotg->host_wakelock, WAKE_LOCK_SUSPEND, "host_wakelock"); init_completion(&dotg->dwc3_xcvr_vbus_init); INIT_DELAYED_WORK(&dotg->sm_work, dwc3_otg_sm_work); INIT_DELAYED_WORK(&dotg->no_device_work, dwc3_otg_no_device_work); no_device_timeout = DWC3_OTG_TIME_NO_DEVICE; stop_host_retry_max = DWC3_OTG_STOP_HOST_RETRY_MAX; no_device_timeout_enable = true; dbg_event(0xFF, "OTGInit get", 0); pm_runtime_get(dwc->dev); return 0; }
/** * dwc3_otg_exit * @dwc: Pointer to out controller context structure * * Returns 0 on success otherwise negative errno. */ void dwc3_otg_exit(struct dwc3 *dwc) { struct dwc3_otg *dotg = dwc->dotg; /* dotg is null when GHWPARAMS6[10]=SRPSupport=0, see dwc3_otg_init */ if (dotg) { if (dotg->charger) dotg->charger->start_detection(dotg->charger, false); cancel_delayed_work_sync(&dotg->sm_work); dbg_event(0xFF, "OTGExit put", 0); pm_runtime_put(dwc->dev); dwc->dotg = NULL; } }
static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, u32 len, u32 type) { 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) { dev_vdbg(dwc->dev, "%s: still busy\n", dep->name); return 0; } trb = dwc->ep0_trb; 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_LST | DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_ISP_IMI); memset(¶ms, 0, sizeof(params)); params.param0 = upper_32_bits(dwc->ep0_trb_addr); params.param1 = lower_32_bits(dwc->ep0_trb_addr); ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_STARTTRANSFER, ¶ms); if (ret < 0) { dbg_event(dep->number, "STTRAFL", ret); dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n"); 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; }
/** * dwc3_otg_init - Initializes otg related registers * @dwc: Pointer to out controller context structure * * Returns 0 on success otherwise negative errno. */ int dwc3_otg_init(struct dwc3 *dwc) { struct dwc3_otg *dotg; dev_dbg(dwc->dev, "dwc3_otg_init\n"); /* Allocate and init otg instance */ dotg = devm_kzalloc(dwc->dev, sizeof(struct dwc3_otg), GFP_KERNEL); if (!dotg) { dev_err(dwc->dev, "unable to allocate dwc3_otg\n"); return -ENOMEM; } dotg->otg.phy = devm_kzalloc(dwc->dev, sizeof(struct usb_phy), GFP_KERNEL); if (!dotg->otg.phy) { dev_err(dwc->dev, "unable to allocate dwc3_otg.phy\n"); return -ENOMEM; } dotg->otg.phy->otg = &dotg->otg; dotg->otg.phy->dev = dwc->dev; dotg->otg.phy->set_power = dwc3_otg_set_power; dotg->otg.set_peripheral = dwc3_otg_set_peripheral; dotg->otg.phy->set_suspend = dwc3_otg_set_suspend; dotg->otg.phy->state = OTG_STATE_UNDEFINED; dotg->regs = dwc->regs; /* This reference is used by dwc3 modules for checking otg existance */ dwc->dotg = dotg; dotg->dwc = dwc; dotg->otg.phy->dev = dwc->dev; init_completion(&dotg->dwc3_xcvr_vbus_init); INIT_DELAYED_WORK(&dotg->sm_work, dwc3_otg_sm_work); dbg_event(0xFF, "OTGInit get", 0); pm_runtime_get(dwc->dev); return 0; }
static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) { struct dwc3_gadget_ep_cmd_params params; u32 cmd; int ret; if (!dep->resource_index) return; cmd = DWC3_DEPCMD_ENDTRANSFER; cmd |= DWC3_DEPCMD_CMDIOC; cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); memset(¶ms, 0, sizeof(params)); ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); if (ret) { dev_dbg(dwc->dev, "%s: send ep cmd ENDTRANSFER failed", dep->name); dbg_event(dep->number, "EENDXFER", ret); } dep->resource_index = 0; }
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; 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) { dev_dbg(dwc->dev, "Invalid Test #%d\n", dwc->test_mode_nr); dbg_event(0x00, "INVALTEST", ret); dwc3_ep0_stall_and_restart(dwc); return; } } status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (status == DWC3_TRBSTS_SETUP_PENDING) dev_dbg(dwc->dev, "Setup Pending received\n"); dbg_print(dep->number, "DONE", status, "STATUS"); dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); }
static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { struct usb_ctrlrequest *ctrl = dwc->ctrl_req; int ret = -EINVAL; u32 len; if (!dwc->gadget_driver) goto out; len = le16_to_cpu(ctrl->wLength); if (!len) { dwc->three_stage_setup = false; dwc->ep0_expect_in = false; dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; } else { dwc->three_stage_setup = true; dwc->ep0_expect_in = !!(ctrl->bRequestType & USB_DIR_IN); dwc->ep0_next_event = DWC3_EP0_NRDY_DATA; } dbg_setup(0x00, ctrl); if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) ret = dwc3_ep0_std_request(dwc, ctrl); else ret = dwc3_ep0_delegate_req(dwc, ctrl); if (ret == USB_GADGET_DELAYED_STATUS) dwc->delayed_status = true; out: if (ret < 0) { dbg_event(0x0, "ERRSTAL", ret); dwc3_ep0_stall_and_restart(dwc); } }
static void dwc3_ep0_xfernotready(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { u8 epnum; int ret; dwc->setup_packet_pending = true; epnum = event->endpoint_number; switch (event->status) { case DEPEVT_STATUS_CONTROL_DATA: dev_vdbg(dwc->dev, "Control Data\n"); /* * 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]; dev_vdbg(dwc->dev, "Wrong direction for Data phase\n"); dwc3_ep0_end_control_data(dwc, dep); dwc3_ep0_stall_and_restart(dwc); return; } if (zlp_required) { zlp_required = false; ret = dwc3_ep0_start_trans(dwc, epnum, dwc->ctrl_req_addr, 0, DWC3_TRBCTL_CONTROL_DATA); dbg_event(epnum, "ZLP", ret); WARN_ON(ret < 0); } break; case DEPEVT_STATUS_CONTROL_STATUS: if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) return; dev_vdbg(dwc->dev, "Control Status\n"); zlp_required = false; dwc->ep0state = EP0_STATUS_PHASE; if (dwc->delayed_status && list_empty(&dwc->eps[0]->request_list)) { WARN_ON_ONCE(event->endpoint_number != 1); dev_vdbg(dwc->dev, "Mass Storage delayed status\n"); return; } dwc->delayed_status = false; dwc3_ep0_do_control_status(dwc, event); } }
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; r = next_request(&ep0->request_list); if (r == NULL) return; ur = &r->request; if ((epnum & 1) && ur->zero && (ur->length % ep0->endpoint.maxpacket == 0)) { zlp_required = true; ur->zero = false; } trb = dwc->ep0_trb; status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (status == DWC3_TRBSTS_SETUP_PENDING) { dev_dbg(dwc->dev, "Setup Pending received\n"); zlp_required = false; if (r) dwc3_gadget_giveback(ep0, r, -ECONNRESET); return; } if (zlp_required) return; 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 */ dbg_event(epnum, "INDATSTAL", 0); dwc3_ep0_stall_and_restart(dwc); } else { /* * handle the case where we have to send a zero packet. This * seems to be case when req.length > maxpacket. Could it be? */ if (r) dwc3_gadget_giveback(ep0, r, 0); } }
/** * dwc3_otg_sm_work - workqueue function. * * @w: Pointer to the dwc3 otg workqueue * * NOTE: After any change in phy->state, * we must reschdule the state machine. */ static void dwc3_otg_sm_work(struct work_struct *w) { struct dwc3_otg *dotg = container_of(w, struct dwc3_otg, sm_work.work); struct usb_phy *phy = dotg->otg.phy; struct dwc3_charger *charger = dotg->charger; bool work = 0; int ret = 0; unsigned long delay = 0; pm_runtime_resume(phy->dev); dev_dbg(phy->dev, "%s state\n", usb_otg_state_string(phy->state)); /* Check OTG state */ switch (phy->state) { case OTG_STATE_UNDEFINED: dwc3_otg_init_sm(dotg); if (!dotg->psy) { dotg->psy = power_supply_get_by_name("usb"); if (!dotg->psy) dev_err(phy->dev, "couldn't get usb power supply\n"); } /* Switch to A or B-Device according to ID / BSV */ if (!test_bit(ID, &dotg->inputs)) { dev_dbg(phy->dev, "!id\n"); phy->state = OTG_STATE_A_IDLE; work = 1; } else if (test_bit(B_SESS_VLD, &dotg->inputs)) { dev_dbg(phy->dev, "b_sess_vld\n"); phy->state = OTG_STATE_B_IDLE; work = 1; } else { phy->state = OTG_STATE_B_IDLE; dev_dbg(phy->dev, "No device, trying to suspend\n"); dbg_event(0xFF, "UNDEF put", 0); pm_runtime_put_sync(phy->dev); } break; case OTG_STATE_B_IDLE: if (!test_bit(ID, &dotg->inputs)) { dev_dbg(phy->dev, "!id\n"); phy->state = OTG_STATE_A_IDLE; work = 1; dotg->charger_retry_count = 0; if (charger) { if (charger->chg_type == DWC3_INVALID_CHARGER) charger->start_detection(dotg->charger, false); else charger->chg_type = DWC3_INVALID_CHARGER; } } else if (test_bit(B_SESS_VLD, &dotg->inputs)) { dev_dbg(phy->dev, "b_sess_vld\n"); if (charger) { /* Has charger been detected? If no detect it */ switch (charger->chg_type) { case DWC3_DCP_CHARGER: case DWC3_PROPRIETARY_CHARGER: dev_dbg(phy->dev, "lpm, DCP charger\n"); dwc3_otg_set_power(phy, dcp_max_current); dbg_event(0xFF, "PROPCHG put", 0); pm_runtime_put_sync(phy->dev); break; case DWC3_CDP_CHARGER: dwc3_otg_set_power(phy, DWC3_IDEV_CHG_MAX); dwc3_otg_start_peripheral(&dotg->otg, 1); phy->state = OTG_STATE_B_PERIPHERAL; work = 1; break; case DWC3_SDP_CHARGER: dwc3_otg_start_peripheral(&dotg->otg, 1); phy->state = OTG_STATE_B_PERIPHERAL; work = 1; break; case DWC3_FLOATED_CHARGER: if (dotg->charger_retry_count < max_chgr_retry_count) dotg->charger_retry_count++; /* * In case of floating charger, if * retry count equal to max retry count * notify PMIC about floating charger * and put Hw in low power mode. Else * perform charger detection again by * calling start_detection() with false * and then with true argument. */ if (dotg->charger_retry_count == max_chgr_retry_count) { dwc3_otg_set_power(phy, 0); dbg_event(0xFF, "FLCHG put", 0); pm_runtime_put_sync(phy->dev); break; } charger->start_detection(dotg->charger, false); default: dev_dbg(phy->dev, "chg_det started\n"); charger->start_detection(charger, true); break; } } else { /* * no charger registered, assuming SDP * and start peripheral */ phy->state = OTG_STATE_B_PERIPHERAL; if (dwc3_otg_start_peripheral(&dotg->otg, 1)) { /* * Probably set_peripheral not called * yet. We will re-try as soon as it * will be called */ dev_err(phy->dev, "enter lpm as\n" "unable to start B-device\n"); phy->state = OTG_STATE_UNDEFINED; dbg_event(0xFF, "NoCH put", 0); pm_runtime_put_sync(phy->dev); return; } } } else { if (charger) charger->start_detection(dotg->charger, false); dotg->charger_retry_count = 0; dwc3_otg_set_power(phy, 0); dev_dbg(phy->dev, "No device, trying to suspend\n"); dbg_event(0xFF, "NoDev put", 0); pm_runtime_put_sync(phy->dev); } break; case OTG_STATE_B_PERIPHERAL: if (!test_bit(B_SESS_VLD, &dotg->inputs) || !test_bit(ID, &dotg->inputs)) { dev_dbg(phy->dev, "!id || !bsv\n"); dwc3_otg_start_peripheral(&dotg->otg, 0); phy->state = OTG_STATE_B_IDLE; if (charger) charger->chg_type = DWC3_INVALID_CHARGER; work = 1; } else if (test_bit(DWC3_OTG_SUSPEND, &dotg->inputs) && test_bit(B_SESS_VLD, &dotg->inputs)) { dbg_event(0xFF, "BPER put", 0); pm_runtime_put_sync(phy->dev); } break; case OTG_STATE_A_IDLE: /* Switch to A-Device*/ if (test_bit(ID, &dotg->inputs)) { dev_dbg(phy->dev, "id\n"); phy->state = OTG_STATE_B_IDLE; dotg->vbus_retry_count = 0; work = 1; } else { phy->state = OTG_STATE_A_HOST; ret = dwc3_otg_start_host(&dotg->otg, 1); if ((ret == -EPROBE_DEFER) && dotg->vbus_retry_count < 3) { /* * Get regulator failed as regulator driver is * not up yet. Will try to start host after 1sec */ phy->state = OTG_STATE_A_IDLE; dev_dbg(phy->dev, "Unable to get vbus regulator. Retrying...\n"); delay = VBUS_REG_CHECK_DELAY; work = 1; dotg->vbus_retry_count++; } else if (ret) { dev_dbg(phy->dev, "enter lpm as\n" "unable to start A-device\n"); phy->state = OTG_STATE_A_IDLE; dbg_event(0xFF, "AIDL put", 0); pm_runtime_put_sync(phy->dev); return; } else { /* * delay 1s to allow for xHCI to detect * just-attached devices before allowing * runtime suspend */ dev_dbg(phy->dev, "a_host state entered\n"); delay = VBUS_REG_CHECK_DELAY; work = 1; } } break; case OTG_STATE_A_HOST: if (test_bit(ID, &dotg->inputs)) { dev_dbg(phy->dev, "id\n"); dwc3_otg_start_host(&dotg->otg, 0); phy->state = OTG_STATE_B_IDLE; dotg->vbus_retry_count = 0; work = 1; } else { dev_dbg(phy->dev, "still in a_host state. Resuming root hub.\n"); dbg_event(0xFF, "AHOST put", 0); pm_runtime_resume(&dotg->dwc->xhci->dev); pm_runtime_put_noidle(phy->dev); } break; default: dev_err(phy->dev, "%s: invalid otg-state\n", __func__); } if (work) queue_delayed_work(system_nrt_wq, &dotg->sm_work, delay); }
/** * dwc3_otg_start_host - helper function for starting/stoping the host controller driver. * * @otg: Pointer to the otg_transceiver structure. * @on: start / stop the host controller driver. * * Returns 0 on success otherwise negative errno. */ static int dwc3_otg_start_host(struct usb_otg *otg, int on) { struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg); struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv; struct dwc3 *dwc = dotg->dwc; struct usb_hcd *hcd; int ret = 0; if (!dwc->xhci) return -EINVAL; if (!dotg->vbus_otg) { dotg->vbus_otg = devm_regulator_get(dwc->dev->parent, "vbus_dwc3"); if (IS_ERR(dotg->vbus_otg)) { dev_err(dwc->dev, "Failed to get vbus regulator\n"); ret = PTR_ERR(dotg->vbus_otg); dotg->vbus_otg = 0; return ret; } } if (on) { dev_dbg(otg->phy->dev, "%s: turn on host\n", __func__); dwc3_otg_notify_host_mode(otg, on); usb_phy_notify_connect(dotg->dwc->usb2_phy, USB_SPEED_HIGH); ret = regulator_enable(dotg->vbus_otg); if (ret) { dev_err(otg->phy->dev, "unable to enable vbus_otg\n"); dwc3_otg_notify_host_mode(otg, 0); return ret; } dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); /* * FIXME If micro A cable is disconnected during system suspend, * xhci platform device will be removed before runtime pm is * enabled for xhci device. Due to this, disable_depth becomes * greater than one and runtimepm is not enabled for next microA * connect. Fix this by calling pm_runtime_init for xhci device. */ pm_runtime_init(&dwc->xhci->dev); ret = platform_device_add(dwc->xhci); if (ret) { dev_err(otg->phy->dev, "%s: failed to add XHCI pdev ret=%d\n", __func__, ret); regulator_disable(dotg->vbus_otg); dwc3_otg_notify_host_mode(otg, 0); return ret; } /* * WORKAROUND: currently host mode suspend isn't working well. * Disable xHCI's runtime PM for now. */ pm_runtime_disable(&dwc->xhci->dev); hcd = platform_get_drvdata(dwc->xhci); otg->host = &hcd->self; dwc3_gadget_usb3_phy_suspend(dwc, true); } else { dev_dbg(otg->phy->dev, "%s: turn off host\n", __func__); ret = regulator_disable(dotg->vbus_otg); if (ret) { dev_err(otg->phy->dev, "unable to disable vbus_otg\n"); return ret; } dbg_event(0xFF, "StHost get", 0); pm_runtime_get(dwc->dev); usb_phy_notify_disconnect(dotg->dwc->usb2_phy, USB_SPEED_HIGH); dwc3_otg_notify_host_mode(otg, on); otg->host = NULL; platform_device_del(dwc->xhci); /* * Perform USB hardware RESET (both core reset and DBM reset) * when moving from host to peripheral. This is required for * peripheral mode to work. */ if (ext_xceiv && ext_xceiv->ext_block_reset) ext_xceiv->ext_block_reset(ext_xceiv, true); dwc3_gadget_usb3_phy_suspend(dwc, false); dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); /* re-init core and OTG registers as block reset clears these */ dwc3_post_host_reset_core_init(dwc); dbg_event(0xFF, "StHost put", 0); pm_runtime_put(dwc->dev); } return 0; }
/** * dwc3_ext_event_notify - callback to handle events from external transceiver * @otg: Pointer to the otg transceiver structure * @event: Event reported by transceiver * * Returns 0 on success */ static void dwc3_ext_event_notify(struct usb_otg *otg, enum dwc3_ext_events event) { static bool init; struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg); struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv; struct usb_phy *phy = dotg->otg.phy; int ret = 0; /* Flush processing any pending events before handling new ones */ if (init) flush_delayed_work(&dotg->sm_work); if (event == DWC3_EVENT_PHY_RESUME) { if (!pm_runtime_status_suspended(phy->dev)) dev_warn(phy->dev, "PHY_RESUME event out of LPM!!!!\n"); dev_dbg(phy->dev, "ext PHY_RESUME event received\n"); /* ext_xceiver would have taken h/w out of LPM by now */ ret = pm_runtime_get(phy->dev); dbg_event(0xFF, "PhyRes get", ret); if (ret == -EACCES) { /* pm_runtime_get may fail during system resume with -EACCES error */ pm_runtime_disable(phy->dev); pm_runtime_set_active(phy->dev); pm_runtime_enable(phy->dev); } else if (ret < 0) { dev_warn(phy->dev, "pm_runtime_get failed!\n"); } } else if (event == DWC3_EVENT_XCEIV_STATE) { if (pm_runtime_status_suspended(phy->dev) || atomic_read(&phy->dev->power.usage_count) == 0) { dev_dbg(phy->dev, "ext XCEIV_STATE while runtime_status=%d\n", phy->dev->power.runtime_status); ret = pm_runtime_get(phy->dev); dbg_event(0xFF, "Xceiv get", ret); if (ret < 0) dev_warn(phy->dev, "pm_runtime_get failed!!\n"); } if (ext_xceiv->id == DWC3_ID_FLOAT) { dev_dbg(phy->dev, "XCVR: ID set\n"); set_bit(ID, &dotg->inputs); } else { dev_dbg(phy->dev, "XCVR: ID clear\n"); clear_bit(ID, &dotg->inputs); } if (ext_xceiv->bsv) { dev_dbg(phy->dev, "XCVR: BSV set\n"); set_bit(B_SESS_VLD, &dotg->inputs); } else { dev_dbg(phy->dev, "XCVR: BSV clear\n"); clear_bit(B_SESS_VLD, &dotg->inputs); } if (!init) { init = true; if (!work_busy(&dotg->sm_work.work)) queue_delayed_work(system_nrt_wq, &dotg->sm_work, 0); complete(&dotg->dwc3_xcvr_vbus_init); dev_dbg(phy->dev, "XCVR: BSV init complete\n"); return; } queue_delayed_work(system_nrt_wq, &dotg->sm_work, 0); } }
/** * dwc3_otg_init - Initializes otg related registers * @dwc: Pointer to out controller context structure * * Returns 0 on success otherwise negative errno. */ int dwc3_otg_init(struct dwc3 *dwc) { u32 reg; int ret = 0; struct dwc3_otg *dotg; dev_dbg(dwc->dev, "dwc3_otg_init\n"); /* Allocate and init otg instance */ dotg = devm_kzalloc(dwc->dev, sizeof(struct dwc3_otg), GFP_KERNEL); if (!dotg) { dev_err(dwc->dev, "unable to allocate dwc3_otg\n"); return -ENOMEM; } dotg->otg.phy = devm_kzalloc(dwc->dev, sizeof(struct usb_phy), GFP_KERNEL); if (!dotg->otg.phy) { dev_err(dwc->dev, "unable to allocate dwc3_otg.phy\n"); return -ENOMEM; } dotg->otg.phy->otg = &dotg->otg; dotg->otg.phy->dev = dwc->dev; dotg->otg.phy->set_power = dwc3_otg_set_power; dotg->otg.set_peripheral = dwc3_otg_set_peripheral; dotg->otg.set_host = dwc3_otg_set_host; dotg->otg.phy->set_suspend = dwc3_otg_set_suspend; dotg->otg.phy->state = OTG_STATE_UNDEFINED; /* * GHWPARAMS6[10] bit is SRPSupport. * This bit also reflects DWC_USB3_EN_OTG */ reg = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6); if (!(reg & DWC3_GHWPARAMS6_SRP_SUPPORT)) { /* * No OTG support in the HW core. * Continue otg_init as currently we don't have Device only mode * support. */ dev_dbg(dwc->dev, "dwc3_otg address space is not supported\n"); } /* DWC3 has separate IRQ line for OTG events (ID/BSV etc.) */ dotg->irq = platform_get_irq_byname(to_platform_device(dwc->dev), "otg_irq"); if (dotg->irq < 0) { dev_err(dwc->dev, "%s: missing OTG IRQ\n", __func__); return -ENODEV; } dotg->regs = dwc->regs; /* This reference is used by dwc3 modules for checking otg existance */ dwc->dotg = dotg; dotg->dwc = dwc; dotg->otg.phy->dev = dwc->dev; init_completion(&dotg->dwc3_xcvr_vbus_init); INIT_DELAYED_WORK(&dotg->sm_work, dwc3_otg_sm_work); ret = request_irq(dotg->irq, dwc3_otg_interrupt, IRQF_SHARED, "dwc3_otg", dotg); if (ret) { dev_err(dotg->otg.phy->dev, "failed to request irq #%d --> %d\n", dotg->irq, ret); goto err1; } dbg_event(0xFF, "OTGInit get", 0); pm_runtime_get(dwc->dev); return 0; err1: cancel_delayed_work_sync(&dotg->sm_work); dwc->dotg = NULL; return ret; }
int main(int argc, char ** argv) { int event, arg; struct FRAME r; int len = 0; int i; protocol_init(argc, argv); lprintf("Designed by JackalDire\n"); disable_network_layer(); for (;;) { event = wait_for_event(&arg); switch (event) { case NETWORK_LAYER_READY: get_packet(buffer[next_frame_to_send]); // fetch new packet from physical layer nbuffered++; send_data_frame(); inc(&next_frame_to_send); break; case PHYSICAL_LAYER_READY: phl_ready = 1; break; case FRAME_RECEIVED: len = recv_frame((unsigned char *)&r, sizeof r); if (len < 5 || crc32((unsigned char *)&r, len) != 0) { /* discard the error packet with wrong crc checksum */ /* TODO : add NAK to accelerate resending */ dbg_event("**** receiver error, bad CRC checksum, packet discarded.\n"); break; } if (r.kind == FRAME_DATA) { dbg_frame("Recv DATA %d %d, ID %d\n", r.seq, r.ack, *(short *)r.data); if (r.seq == frame_expected) { /* transmit the receive packet to network layer */ put_packet(r.data, PKT_LEN); // 7 = 3B other fields + 4B crc code inc(&frame_expected); start_ack_timer(ACK_TIMER); } } /* ACK n means packets earlier than n were received correctly, * so make packet ack_expeced ~ r.ack acknowledged */ while (between(ack_expected, r.ack, next_frame_to_send)) { --nbuffered; stop_timer(ack_expected); inc(&ack_expected); } break; case DATA_TIMEOUT: dbg_event("---- DATA %d timeout\n", arg); next_frame_to_send = ack_expected; /* retransmit all packets in the buffer when timeout */ for (i = 1; i <= nbuffered; ++i) { send_data_frame(); inc(&next_frame_to_send); } break; case ACK_TIMEOUT: /* send a separate ACK frame if there is no outstream for a specific time */ dbg_event("---- ACK %d timeout\n", arg); send_ack_frame(); } /* disable networklayer when outstrem buffer if full */ if (nbuffered < MAX_SEQ && phl_ready) enable_network_layer(); else disable_network_layer(); } }
/** * dwc3_otg_start_host - helper function for starting/stoping the host controller driver. * * @otg: Pointer to the otg_transceiver structure. * @on: start / stop the host controller driver. * * Returns 0 on success otherwise negative errno. */ static int dwc3_otg_start_host(struct usb_otg *otg, int on) { struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg); struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv; struct dwc3 *dwc = dotg->dwc; struct usb_hcd *hcd; int ret = 0; if (!dwc->xhci) return -EINVAL; if (!dotg->vbus_otg) { dotg->vbus_otg = devm_regulator_get(dwc->dev->parent, "vbus_dwc3"); if (IS_ERR(dotg->vbus_otg)) { dev_err(dwc->dev, "Failed to get vbus regulator\n"); ret = PTR_ERR(dotg->vbus_otg); dotg->vbus_otg = 0; return ret; } } if (on) { dev_dbg(otg->phy->dev, "%s: turn on host\n", __func__); dwc3_otg_notify_host_mode(otg, on); usb_phy_notify_connect(dotg->dwc->usb2_phy, USB_SPEED_HIGH); /* register ocp notification */ if (ext_xceiv && ext_xceiv->ext_ocp_notification.notify) { ret = regulator_register_ocp_notification( dotg->vbus_otg, &ext_xceiv->ext_ocp_notification); if (ret) dev_err(otg->phy->dev, "unable to register ocp\n"); } ret = regulator_enable(dotg->vbus_otg); if (ret) { dev_err(otg->phy->dev, "unable to enable vbus_otg\n"); dwc3_otg_notify_host_mode(otg, 0); return ret; } /* The delay between enabling regulator and adding the platform device is needed to succeed in the enumeration for certain devices. */ usleep(10000); dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); /* * FIXME If micro A cable is disconnected during system suspend, * xhci platform device will be removed before runtime pm is * enabled for xhci device. Due to this, disable_depth becomes * greater than one and runtimepm is not enabled for next microA * connect. Fix this by calling pm_runtime_init for xhci device. */ pm_runtime_init(&dwc->xhci->dev); ret = platform_device_add(dwc->xhci); if (ret) { dev_err(otg->phy->dev, "%s: failed to add XHCI pdev ret=%d\n", __func__, ret); regulator_disable(dotg->vbus_otg); dwc3_otg_notify_host_mode(otg, 0); return ret; } /* * WORKAROUND: avoids entering a suspend state, because PMIC * is damaged by known issue when attempt to suspend. */ wake_lock(&dotg->host_wakelock); /* * WORKAROUND: currently host mode suspend isn't working well. * Disable xHCI's runtime PM for now. */ pm_runtime_disable(&dwc->xhci->dev); if (no_device_timeout_enable) { dotg->no_device_timeout_enabled = 1; dotg->device_count = 0; dotg->retry_count = 0; dotg->usbdev_nb.notifier_call = dwc3_usbdev_notify; usb_register_notify(&dotg->usbdev_nb); dwc3_otg_start_no_device_work(dotg, true); } hcd = platform_get_drvdata(dwc->xhci); otg->host = &hcd->self; dwc3_gadget_usb3_phy_suspend(dwc, true); } else { dev_dbg(otg->phy->dev, "%s: turn off host\n", __func__); ret = regulator_disable(dotg->vbus_otg); if (ret) { dev_err(otg->phy->dev, "unable to disable vbus_otg\n"); return ret; } dbg_event(0xFF, "StHost get", 0); /* unregister ocp notification */ if (ext_xceiv && ext_xceiv->ext_ocp_notification.notify) { ret = regulator_register_ocp_notification( dotg->vbus_otg, NULL); if (ret) dev_err(otg->phy->dev, "unable to unregister ocp\n"); } pm_runtime_get(dwc->dev); usb_phy_notify_disconnect(dotg->dwc->usb2_phy, USB_SPEED_HIGH); dwc3_otg_notify_host_mode(otg, on); otg->host = NULL; if (dotg->no_device_timeout_enabled) { dotg->no_device_timeout_enabled = 0; dwc3_otg_start_no_device_work(dotg, false); usb_unregister_notify(&dotg->usbdev_nb); } platform_device_del(dwc->xhci); /* WORKAROUND: delays execution of the block-reset because it * causes HWWD if ext_xceive access the HW during block reset. */ usleep(10000); if (wake_lock_active(&dotg->host_wakelock)) wake_unlock(&dotg->host_wakelock); /* * Perform USB hardware RESET (both core reset and DBM reset) * when moving from host to peripheral. This is required for * peripheral mode to work. */ if (ext_xceiv && ext_xceiv->ext_block_reset) ext_xceiv->ext_block_reset(ext_xceiv, true); dwc3_gadget_usb3_phy_suspend(dwc, false); dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); /* re-init core and OTG registers as block reset clears these */ dwc3_post_host_reset_core_init(dwc); dbg_event(0xFF, "StHost put", 0); pm_runtime_put(dwc->dev); } return 0; }