diff options
| author | Mayank Rana <mrana@codeaurora.org> | 2016-10-06 10:57:39 -0700 |
|---|---|---|
| committer | Hemant Kumar <hemantk@codeaurora.org> | 2017-02-27 18:30:59 -0800 |
| commit | 82e6740d7f3c556fa84166874661f184eaec0c30 (patch) | |
| tree | 9942ef62914ba424b6398d18f7b0067b9a8dd3a3 /drivers/usb | |
| parent | 0d11e5fbc021c7b05e6b4cb4d5e0a4c7ab29ef1f (diff) | |
f_gsi: Don't send end xfer command when USB is into U3 state
It is recommended to avoid sending end xfer command with USB GSI
endpoint when USB is into U3 state (i.e. bus suspended) as USB
controller may hang. USB GSI driver sends end xfer command with
suspend implementation when remote wakeup is not allowed. Hence
update IPA driver about remote wakeup status to suspend or
disconnect IPA GSI channel whereas remove sending end xfer command
to USB controller when remote wakeup is not allowed.
CRs-Fixed: 1077546
Change-Id: I3ac3714ccaf4ac9f16c26f59b9550aa825483344
Signed-off-by: Mayank Rana <mrana@codeaurora.org>
Diffstat (limited to 'drivers/usb')
| -rw-r--r-- | drivers/usb/gadget/function/f_gsi.c | 187 |
1 files changed, 70 insertions, 117 deletions
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index 9473e7e31eaa..d3845b4f82c6 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -40,7 +40,6 @@ MODULE_PARM_DESC(qti_packet_debug, "Print QTI Packet's Raw Data"); static struct workqueue_struct *ipa_usb_wq; -static void gsi_rndis_ipa_reset_trigger(struct f_gsi *rndis); static void ipa_disconnect_handler(struct gsi_data_port *d_port); static int gsi_ctrl_send_notification(struct f_gsi *gsi); static int gsi_alloc_trb_buffer(struct f_gsi *gsi); @@ -48,6 +47,20 @@ static void gsi_free_trb_buffer(struct f_gsi *gsi); static struct gsi_ctrl_pkt *gsi_ctrl_pkt_alloc(unsigned len, gfp_t flags); static void gsi_ctrl_pkt_free(struct gsi_ctrl_pkt *pkt); +static inline bool usb_gsi_remote_wakeup_allowed(struct usb_function *f) +{ + bool remote_wakeup_allowed; + + if (f->config->cdev->gadget->speed == USB_SPEED_SUPER) + remote_wakeup_allowed = f->func_wakeup_allowed; + else + remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup; + + log_event_dbg("%s: remote_wakeup_allowed:%s", __func__, + remote_wakeup_allowed ? "true" : "false"); + return remote_wakeup_allowed; +} + void post_event(struct gsi_data_port *port, u8 event) { unsigned long flags; @@ -477,16 +490,22 @@ static void ipa_disconnect_handler(struct gsi_data_port *d_port) log_event_dbg("%s: EP Disable for data", __func__); - /* Block doorbell to GSI to avoid USB wrapper from - * ringing doorbell in case IPA clocks are OFF - */ - usb_gsi_ep_op(d_port->in_ep, (void *)&block_db, + if (gsi->d_port.in_ep) { + /* + * Block doorbell to GSI to avoid USB wrapper from + * ringing doorbell in case IPA clocks are OFF. + */ + usb_gsi_ep_op(d_port->in_ep, (void *)&block_db, GSI_EP_OP_SET_CLR_BLOCK_DBL); + gsi->in_ep_desc_backup = gsi->d_port.in_ep->desc; + usb_gsi_ep_op(gsi->d_port.in_ep, NULL, GSI_EP_OP_DISABLE); + } - usb_gsi_ep_op(gsi->d_port.in_ep, NULL, GSI_EP_OP_DISABLE); - - if (gsi->d_port.out_ep) + if (gsi->d_port.out_ep) { + gsi->out_ep_desc_backup = gsi->d_port.out_ep->desc; usb_gsi_ep_op(gsi->d_port.out_ep, NULL, GSI_EP_OP_DISABLE); + } + gsi->d_port.net_ready_trigger = false; } @@ -523,19 +542,21 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port) int ret = 0; bool block_db, f_suspend; struct f_gsi *gsi = d_port_to_gsi(d_port); + struct usb_function *f = &gsi->function; + + f_suspend = f->func_wakeup_allowed; + log_event_dbg("%s: f_suspend:%d", __func__, f_suspend); - f_suspend = gsi->function.func_wakeup_allowed; if (!usb_gsi_ep_op(gsi->d_port.in_ep, (void *) &f_suspend, GSI_EP_OP_CHECK_FOR_SUSPEND)) { ret = -EFAULT; goto done; } - log_event_dbg("%s: Calling xdci_suspend", __func__); + log_event_dbg("%s: Calling xdci_suspend", __func__); ret = ipa_usb_xdci_suspend(gsi->d_port.out_channel_handle, gsi->d_port.in_channel_handle, gsi->prot_id, - true); - + usb_gsi_remote_wakeup_allowed(f)); if (!ret) { d_port->sm_state = STATE_SUSPENDED; log_event_dbg("%s: STATE SUSPENDED", __func__); @@ -553,10 +574,8 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port) log_event_err("%s: Error %d for %d", __func__, ret, gsi->prot_id); } - - log_event_dbg("%s: xdci_suspend ret %d", __func__, ret); - done: + log_event_dbg("%s: xdci_suspend ret %d", __func__, ret); return ret; } @@ -591,7 +610,6 @@ static void ipa_work_handler(struct work_struct *w) struct device *dev; struct device *gad_dev; struct f_gsi *gsi; - bool block_db; event = read_event(d_port); @@ -653,6 +671,27 @@ static void ipa_work_handler(struct work_struct *w) __func__); break; } + + if (d_port->out_ep && !d_port->out_ep->desc && + gsi->out_ep_desc_backup) { + d_port->out_ep->desc = gsi->out_ep_desc_backup; + d_port->out_ep->ep_intr_num = 1; + } + + if (d_port->in_ep && !d_port->in_ep->desc && + gsi->in_ep_desc_backup) { + d_port->in_ep->desc = gsi->in_ep_desc_backup; + d_port->in_ep->ep_intr_num = 2; + } + + if (d_port->out_ep) + usb_gsi_ep_op(d_port->out_ep, + &d_port->out_request, GSI_EP_OP_CONFIG); + + if (d_port->in_ep) + usb_gsi_ep_op(d_port->in_ep, + &d_port->in_request, GSI_EP_OP_CONFIG); + ipa_connect_channels(d_port); ipa_data_path_enable(d_port); d_port->sm_state = STATE_CONNECTED; @@ -714,15 +753,7 @@ static void ipa_work_handler(struct work_struct *w) if (event == EVT_HOST_NRDY) { log_event_dbg("%s: ST_CON_HOST_NRDY\n", __func__); - block_db = true; - /* stop USB ringing doorbell to GSI(OUT_EP) */ - usb_gsi_ep_op(d_port->in_ep, (void *)&block_db, - GSI_EP_OP_SET_CLR_BLOCK_DBL); - gsi_rndis_ipa_reset_trigger(gsi); - usb_gsi_ep_op(d_port->in_ep, NULL, - GSI_EP_OP_ENDXFER); - usb_gsi_ep_op(d_port->out_ep, NULL, - GSI_EP_OP_ENDXFER); + ipa_disconnect_handler(d_port); } ipa_disconnect_work_handler(d_port); @@ -1345,26 +1376,6 @@ static void gsi_rndis_open(struct f_gsi *rndis) rndis_signal_connect(rndis->params); } -static void gsi_rndis_ipa_reset_trigger(struct f_gsi *rndis) -{ - unsigned long flags; - - if (!rndis) { - log_event_err("%s: gsi prot ctx is %pK", __func__, rndis); - return; - } - - spin_lock_irqsave(&rndis->d_port.lock, flags); - if (!rndis) { - log_event_err("%s: No RNDIS instance", __func__); - spin_unlock_irqrestore(&rndis->d_port.lock, flags); - return; - } - - rndis->d_port.net_ready_trigger = false; - spin_unlock_irqrestore(&rndis->d_port.lock, flags); -} - void gsi_rndis_flow_ctrl_enable(bool enable, struct rndis_params *param) { struct f_gsi *rndis = param->v; @@ -2115,7 +2126,6 @@ static void gsi_suspend(struct usb_function *f) { bool block_db; struct f_gsi *gsi = func_to_gsi(f); - bool remote_wakeup_allowed; /* Check if function is already suspended in gsi_func_suspend() */ if (f->func_is_suspended) { @@ -2123,49 +2133,17 @@ static void gsi_suspend(struct usb_function *f) return; } - if (f->config->cdev->gadget->speed == USB_SPEED_SUPER) - remote_wakeup_allowed = f->func_wakeup_allowed; - else - remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup; - - log_event_info("%s: remote_wakeup_allowed %d", - __func__, remote_wakeup_allowed); - - if (!remote_wakeup_allowed) { - if (gsi->prot_id == IPA_USB_RNDIS) - rndis_flow_control(gsi->params, true); - /* - * When remote wakeup is disabled, IPA is disconnected - * because it cannot send new data until the USB bus is - * resumed. Endpoint descriptors info is saved before it - * gets reset by the BAM disconnect API. This lets us - * restore this info when the USB bus is resumed. - */ - if (gsi->d_port.in_ep) - gsi->in_ep_desc_backup = gsi->d_port.in_ep->desc; - if (gsi->d_port.out_ep) - gsi->out_ep_desc_backup = gsi->d_port.out_ep->desc; - - ipa_disconnect_handler(&gsi->d_port); - - post_event(&gsi->d_port, EVT_DISCONNECTED); - queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w); - log_event_dbg("%s: Disconnecting", __func__); - } else { - block_db = true; - usb_gsi_ep_op(gsi->d_port.in_ep, (void *)&block_db, - GSI_EP_OP_SET_CLR_BLOCK_DBL); - post_event(&gsi->d_port, EVT_SUSPEND); - queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w); - } - + block_db = true; + usb_gsi_ep_op(gsi->d_port.in_ep, (void *)&block_db, + GSI_EP_OP_SET_CLR_BLOCK_DBL); + post_event(&gsi->d_port, EVT_SUSPEND); + queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w); log_event_dbg("gsi suspended"); } static void gsi_resume(struct usb_function *f) { struct f_gsi *gsi = func_to_gsi(f); - bool remote_wakeup_allowed; struct usb_composite_dev *cdev = f->config->cdev; log_event_dbg("%s", __func__); @@ -2178,49 +2156,24 @@ static void gsi_resume(struct usb_function *f) f->func_is_suspended) return; - if (f->config->cdev->gadget->speed == USB_SPEED_SUPER) - remote_wakeup_allowed = f->func_wakeup_allowed; - else - remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup; - if (gsi->c_port.notify && !gsi->c_port.notify->desc) config_ep_by_speed(cdev->gadget, f, gsi->c_port.notify); /* Check any pending cpkt, and queue immediately on resume */ gsi_ctrl_send_notification(gsi); - if (!remote_wakeup_allowed) { - - /* Configure EPs for GSI */ - if (gsi->d_port.out_ep) { - gsi->d_port.out_ep->desc = gsi->out_ep_desc_backup; - gsi->d_port.out_ep->ep_intr_num = 1; - usb_gsi_ep_op(gsi->d_port.out_ep, - &gsi->d_port.out_request, GSI_EP_OP_CONFIG); - } - gsi->d_port.in_ep->desc = gsi->in_ep_desc_backup; - if (gsi->prot_id != IPA_USB_DIAG) - gsi->d_port.in_ep->ep_intr_num = 2; - else - gsi->d_port.in_ep->ep_intr_num = 3; - - usb_gsi_ep_op(gsi->d_port.in_ep, &gsi->d_port.in_request, - GSI_EP_OP_CONFIG); - post_event(&gsi->d_port, EVT_CONNECT_IN_PROGRESS); - - /* - * Linux host does not send RNDIS_MSG_INIT or non-zero - * RNDIS_MESSAGE_PACKET_FILTER after performing bus resume. - * Trigger state machine explicitly on resume. - */ - if (gsi->prot_id == IPA_USB_RNDIS) - rndis_flow_control(gsi->params, false); - } else - post_event(&gsi->d_port, EVT_RESUMED); + /* + * Linux host does not send RNDIS_MSG_INIT or non-zero + * RNDIS_MESSAGE_PACKET_FILTER after performing bus resume. + * Trigger state machine explicitly on resume. + */ + if (gsi->prot_id == IPA_USB_RNDIS && + !usb_gsi_remote_wakeup_allowed(f)) + rndis_flow_control(gsi->params, false); + post_event(&gsi->d_port, EVT_RESUMED); queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w); - log_event_dbg("%s: completed", __func__); } |
