This patch implements OTG host request polling and does role switch when peripheral sets the host request flag. Signed-off-by: Li Jun <b47624@xxxxxxxxxxxxx> --- drivers/usb/chipidea/ci.h | 1 + drivers/usb/chipidea/otg_fsm.c | 81 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/chipidea/otg_fsm.h | 4 ++ 3 files changed, 86 insertions(+), 0 deletions(-) diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 8152020..8d6032e 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -205,6 +205,7 @@ struct ci_hdrc { struct dentry *debugfs; bool id_event; bool b_sess_valid_event; + bool hnp_polling_event; bool imx28_write_fix; }; diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index 613eb86..2586cdb 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -463,6 +463,14 @@ static void ci_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t) return; } +static void ci_otg_start_hnp_polling(struct otg_fsm *fsm) +{ + mod_timer(&fsm->hnp_polling_timer, + jiffies + msecs_to_jiffies(T_HOST_REQ_POLL)); + + return; +} + static void ci_otg_chrg_vbus(struct otg_fsm *fsm, int on) { struct ci_hdrc *ci = container_of(fsm->otg->gadget, @@ -613,6 +621,7 @@ static struct otg_fsm_ops ci_otg_ops = { .loc_sof = ci_otg_loc_sof, .start_pulse = ci_otg_start_pulse, .start_adp_prb = ci_otg_start_adp_prb, + .start_hnp_polling = ci_otg_start_hnp_polling, .add_timer = ci_otg_fsm_add_timer, .del_timer = ci_otg_fsm_del_timer, @@ -621,11 +630,80 @@ static struct otg_fsm_ops ci_otg_ops = { .start_gadget = ci_otg_start_gadget, }; +static void hnp_polling_timer_work(unsigned long arg) +{ + struct ci_hdrc *ci = (struct ci_hdrc *)arg; + + ci->hnp_polling_event = true; + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); +} + +static int ci_hnp_polling(struct ci_hdrc *ci) +{ + struct usb_device *udev; + int err = 0; + u16 data; + + if (ci->transceiver->state != OTG_STATE_A_HOST + && ci->transceiver->state != OTG_STATE_B_HOST) + return -EINVAL; + + if (ci->transceiver->otg->host && + ci->transceiver->otg->host->root_hub) { + udev = usb_hub_find_child( + ci->transceiver->otg->host->root_hub, 1); + if (!udev) { + dev_dbg(ci->dev, + "no usb dev connected, stop HNP polling\n"); + return -ENODEV; + } else if (udev->state < USB_STATE_DEFAULT) { + ci_otg_start_hnp_polling(ci->fsm); + return -EAGAIN; + } + } else { + dev_dbg(ci->dev, "no host or root_hub registered\n"); + return -ENODEV; + } + + /* get host request flag from connected USB device */ + err = usb_get_status(udev, USB_RECIP_DEVICE, OTG_STS_SELECTOR, &data); + if (err) { + dev_warn(ci->dev, + "ERR in HNP polling = %d, stop HNP polling\n", err); + return -EINVAL; + } + + if ((data & 0xff) == HOST_REQUEST_FLAG) { + /* Start role switch */ + dev_dbg(ci->dev, "host request flag = 1\n"); + if (ci->transceiver->state == OTG_STATE_A_HOST) + ci->fsm->a_bus_req = 0; + else if (ci->transceiver->state == OTG_STATE_B_HOST) + ci->fsm->b_bus_req = 0; + return 0; + } else if ((data & 0xff) == 0) { + /* Continue polling */ + ci_otg_start_hnp_polling(ci->fsm); + return -EAGAIN; + } else + dev_err(ci->dev, "host request flag is invalid value\n"); + + return err; +} + int ci_otg_fsm_work(struct ci_hdrc *ci) { if (!ci->transceiver->otg || !ci->fsm) return -ENODEV; + if (ci->hnp_polling_event) { + ci->hnp_polling_event = false; + if (ci_hnp_polling(ci)) { + return 0; + } + } + if (otg_statemachine(ci->fsm)) { if (ci->transceiver->state == OTG_STATE_A_IDLE) { if (ci->fsm->id) @@ -869,6 +947,9 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) return retval; } + setup_timer(&ci->fsm->hnp_polling_timer, hnp_polling_timer_work, + (unsigned long)ci); + /* enable ID and A vbus valid irq */ hw_write(ci, OP_OTGSC, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, OTGSC_IDIE | OTGSC_AVVIE); diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h index 6cdd55c2..53f75b2 100644 --- a/drivers/usb/chipidea/otg_fsm.h +++ b/drivers/usb/chipidea/otg_fsm.h @@ -78,6 +78,10 @@ #define TB_SSEND_SRP (1500) /* Table 5-1, Session end to SRP init */ #define TB_SESS_VLD (1000) +#define T_HOST_REQ_POLL (1500) /* Section 6.3.1 HNP polling process + * OTG and EH Revison 2.0 version 1.1a + * July 27, 2012 */ + enum ci_otg_fsm_timer_index { /* CI specific timers, start from the end * of standard and auxiliary OTG timers */ -- 1.7.8 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html