diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index 0c9cd254a62..1507cde1284 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -54,8 +55,8 @@ #define DRIVER_NAME "msm_otg" #define ID_TIMER_FREQ (jiffies + msecs_to_jiffies(2000)) +#define ID_TIMER_INITIAL_FREQ (jiffies + msecs_to_jiffies(1000)) #define ULPI_IO_TIMEOUT_USEC (10 * 1000) - #define USB_PHY_3P3_VOL_MIN 3050000 /* uV */ #define USB_PHY_3P3_VOL_MAX 3300000 /* uV */ #define USB_PHY_3P3_HPM_LOAD 50000 /* uA */ @@ -584,25 +585,190 @@ static int msm_otg_reset(struct usb_phy *phy) return 0; } -static int msm_otg_set_suspend(struct otg_transceiver *otg, int suspend) +static const char *timer_string(int bit) { - struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + switch (bit) { + case A_WAIT_VRISE: return "a_wait_vrise"; + case A_WAIT_VFALL: return "a_wait_vfall"; + case B_SRP_FAIL: return "b_srp_fail"; + case A_WAIT_BCON: return "a_wait_bcon"; + case A_AIDL_BDIS: return "a_aidl_bdis"; + case A_BIDL_ADIS: return "a_bidl_adis"; + case B_ASE0_BRST: return "b_ase0_brst"; + case A_TST_MAINT: return "a_tst_maint"; + case B_TST_SRP: return "b_tst_srp"; + case B_TST_CONFIG: return "b_tst_config"; + default: return "UNDEFINED"; + } +} - /* - * Allow bus suspend only for host mode. Device mode bus suspend - * is not implemented yet. - */ - if (!test_bit(ID, &motg->inputs) || test_bit(ID_A, &motg->inputs)) { +static enum hrtimer_restart msm_otg_timer_func(struct hrtimer *hrtimer) +{ + struct msm_otg *motg = container_of(hrtimer, struct msm_otg, timer); + + switch (motg->active_tmout) { + case A_WAIT_VRISE: + /* TODO: use vbus_vld interrupt */ + set_bit(A_VBUS_VLD, &motg->inputs); + break; + case A_TST_MAINT: + /* OTG PET: End session after TA_TST_MAINT */ + set_bit(A_BUS_DROP, &motg->inputs); + break; + case B_TST_SRP: /* - * ID_GND --> ID_A transition can not be detected in LPM. - * Disallow host bus suspend when ACA is enabled. + * OTG PET: Initiate SRP after TB_TST_SRP of + * previous session end. */ - if (suspend && !aca_enabled()) - pm_runtime_put(otg->dev); - else - pm_runtime_resume(otg->dev); + set_bit(B_BUS_REQ, &motg->inputs); + break; + case B_TST_CONFIG: + clear_bit(A_CONN, &motg->inputs); + break; + default: + set_bit(motg->active_tmout, &motg->tmouts); } + pr_debug("expired %s timer\n", timer_string(motg->active_tmout)); + queue_work(system_nrt_wq, &motg->sm_work); + return HRTIMER_NORESTART; +} + +static void msm_otg_del_timer(struct msm_otg *motg) +{ + int bit = motg->active_tmout; + + pr_debug("deleting %s timer. remaining %lld msec\n", timer_string(bit), + div_s64(ktime_to_us(hrtimer_get_remaining( + &motg->timer)), 1000)); + hrtimer_cancel(&motg->timer); + clear_bit(bit, &motg->tmouts); +} + +static void msm_otg_start_timer(struct msm_otg *motg, int time, int bit) +{ + clear_bit(bit, &motg->tmouts); + motg->active_tmout = bit; + pr_debug("starting %s timer\n", timer_string(bit)); + hrtimer_start(&motg->timer, + ktime_set(time / 1000, (time % 1000) * 1000000), + HRTIMER_MODE_REL); +} + +static void msm_otg_init_timer(struct msm_otg *motg) +{ + hrtimer_init(&motg->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + motg->timer.function = msm_otg_timer_func; +} + +static int msm_otg_start_hnp(struct usb_otg *otg) +{ + struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy); + + if (otg->phy->state != OTG_STATE_A_HOST) { + pr_err("HNP can not be initiated in %s state\n", + otg_state_string(otg->phy->state)); + return -EINVAL; + } + + pr_debug("A-Host: HNP initiated\n"); + clear_bit(A_BUS_REQ, &motg->inputs); + queue_work(system_nrt_wq, &motg->sm_work); + return 0; +} + +static int msm_otg_start_srp(struct usb_otg *otg) +{ + struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy); + u32 val; + int ret = 0; + + if (otg->phy->state != OTG_STATE_B_IDLE) { + pr_err("SRP can not be initiated in %s state\n", + otg_state_string(otg->phy->state)); + ret = -EINVAL; + goto out; + } + + if ((jiffies - motg->b_last_se0_sess) < msecs_to_jiffies(TB_SRP_INIT)) { + pr_debug("initial conditions of SRP are not met. Try again" + "after some time\n"); + ret = -EAGAIN; + goto out; + } + + pr_debug("B-Device SRP started\n"); + + /* + * PHY won't pull D+ high unless it detects Vbus valid. + * Since by definition, SRP is only done when Vbus is not valid, + * software work-around needs to be used to spoof the PHY into + * thinking it is valid. This can be done using the VBUSVLDEXTSEL and + * VBUSVLDEXT register bits. + */ + ulpi_write(otg->phy, 0x03, 0x97); + /* + * Harware auto assist data pulsing: Data pulse is given + * for 7msec; wait for vbus + */ + val = readl_relaxed(USB_OTGSC); + writel_relaxed((val & ~OTGSC_INTSTS_MASK) | OTGSC_HADP, USB_OTGSC); + + /* VBUS plusing is obsoleted in OTG 2.0 supplement */ +out: + return ret; +} + +static void msm_otg_host_hnp_enable(struct usb_otg *otg, bool enable) +{ + struct usb_hcd *hcd = bus_to_hcd(otg->host); + struct usb_device *rhub = otg->host->root_hub; + + if (enable) { + pm_runtime_disable(&rhub->dev); + rhub->state = USB_STATE_NOTATTACHED; + hcd->driver->bus_suspend(hcd); + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + } else { + usb_remove_hcd(hcd); + msm_otg_reset(otg->phy); + usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); + } +} + +static int msm_otg_set_suspend(struct usb_phy *phy, int suspend) +{ + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); + + if (aca_enabled() || (test_bit(ID, &motg->inputs) && + !test_bit(ID_A, &motg->inputs))) + return 0; + + if (suspend) { + switch (phy->state) { + case OTG_STATE_A_WAIT_BCON: + if (TA_WAIT_BCON > 0) + break; + /* fall through */ + case OTG_STATE_A_HOST: + pr_debug("host bus suspend\n"); + clear_bit(A_BUS_REQ, &motg->inputs); + queue_work(system_nrt_wq, &motg->sm_work); + break; + default: + break; + } + } else { + switch (phy->state) { + case OTG_STATE_A_SUSPEND: + /* Remote wakeup or resume */ + set_bit(A_BUS_REQ, &motg->inputs); + phy->state = OTG_STATE_A_HOST; + break; + default: + break; + } + } return 0; } @@ -908,6 +1074,11 @@ psy_not_supported: static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA) { + struct usb_gadget *g = motg->phy.otg->gadget; + + if (g && g->is_a_peripheral) + return; + if ((motg->chg_type == USB_ACA_DOCK_CHARGER || motg->chg_type == USB_ACA_A_CHARGER || motg->chg_type == USB_ACA_B_CHARGER || @@ -952,19 +1123,19 @@ static int msm_otg_set_power(struct usb_phy *phy, unsigned mA) return 0; } -static void msm_otg_start_host(struct usb_phy *phy, int on) +static void msm_otg_start_host(struct usb_otg *otg, int on) { - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); + struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy); struct msm_otg_platform_data *pdata = motg->pdata; struct usb_hcd *hcd; - if (!phy->otg->host) + if (!otg->host) return; - hcd = bus_to_hcd(phy->otg->host); + hcd = bus_to_hcd(otg->host); if (on) { - dev_dbg(phy->dev, "host on\n"); + dev_dbg(otg->phy->dev, "host on\n"); /* * Some boards have a switch cotrolled by gpio @@ -975,7 +1146,7 @@ static void msm_otg_start_host(struct usb_phy *phy, int on) pdata->setup_gpio(OTG_STATE_A_HOST); usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); } else { - dev_dbg(phy->dev, "host off\n"); + dev_dbg(otg->phy->dev, "host off\n"); usb_remove_hcd(hcd); /* HCD core reset all bits of PORTSC. select ULPI phy */ @@ -993,9 +1164,6 @@ static int msm_otg_usbdev_notify(struct notifier_block *self, struct usb_otg *otg = motg->phy.otg; struct usb_device *udev = priv; - if (!aca_enabled()) - goto out; - if (action == USB_BUS_ADD || action == USB_BUS_REMOVE) goto out; @@ -1012,17 +1180,47 @@ static int msm_otg_usbdev_notify(struct notifier_block *self, switch (action) { case USB_DEVICE_ADD: - usb_disable_autosuspend(udev); + if (aca_enabled()) + usb_disable_autosuspend(udev); + if (otg->phy->state == OTG_STATE_A_WAIT_BCON) { + pr_debug("B_CONN set\n"); + set_bit(B_CONN, &motg->inputs); + msm_otg_del_timer(motg); + otg->phy->state = OTG_STATE_A_HOST; + /* + * OTG PET: A-device must end session within + * 10 sec after PET enumeration. + */ + if (udev->quirks & USB_QUIRK_OTG_PET) + msm_otg_start_timer(motg, TA_TST_MAINT, + A_TST_MAINT); + } /* fall through */ case USB_DEVICE_CONFIG: if (udev->actconfig) motg->mA_port = udev->actconfig->desc.bMaxPower * 2; else motg->mA_port = IUNIT; + if (otg->phy->state == OTG_STATE_B_HOST) + msm_otg_del_timer(motg); break; case USB_DEVICE_REMOVE: - motg->mA_port = IUNIT; - break; + if ((otg->phy->state == OTG_STATE_A_HOST) || + (otg->phy->state == OTG_STATE_A_SUSPEND)) { + pr_debug("B_CONN clear\n"); + clear_bit(B_CONN, &motg->inputs); + /* + * OTG PET: A-device must end session after + * PET disconnection if it is enumerated + * with bcdDevice[0] = 1. USB core sets + * bus->otg_vbus_off for us. clear it here. + */ + if (udev->bus->otg_vbus_off) { + udev->bus->otg_vbus_off = 0; + set_bit(A_BUS_DROP, &motg->inputs); + } + queue_work(system_nrt_wq, &motg->sm_work); + } default: break; } @@ -1101,11 +1299,11 @@ static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host) if (otg->phy->state == OTG_STATE_A_HOST) { pm_runtime_get_sync(otg->phy->dev); usb_unregister_notify(&motg->usbdev_nb); - msm_otg_start_host(otg->phy, 0); + msm_otg_start_host(otg, 0); msm_hsusb_vbus_power(motg, 0); otg->host = NULL; otg->phy->state = OTG_STATE_UNDEFINED; - schedule_work(&motg->sm_work); + queue_work(system_nrt_wq, &motg->sm_work); } else { otg->host = NULL; } @@ -1119,6 +1317,9 @@ static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host) hcd = bus_to_hcd(host); hcd->power_budget = motg->pdata->power_budget; +#ifdef CONFIG_USB_OTG + host->otg_port = 1; +#endif motg->usbdev_nb.notifier_call = msm_otg_usbdev_notify; usb_register_notify(&motg->usbdev_nb); otg->host = host; @@ -1130,23 +1331,23 @@ static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host) */ if (motg->pdata->mode == USB_HOST || otg->gadget) { pm_runtime_get_sync(otg->phy->dev); - schedule_work(&motg->sm_work); + queue_work(system_nrt_wq, &motg->sm_work); } return 0; } -static void msm_otg_start_peripheral(struct usb_phy *phy, int on) +static void msm_otg_start_peripheral(struct usb_otg *otg, int on) { int ret; - struct msm_otg *motg = container_of(phy, struct msm_otg, otg); + struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy); struct msm_otg_platform_data *pdata = motg->pdata; - if (!phy->otg->gadget) + if (!otg->gadget) return; if (on) { - dev_dbg(phy->dev, "gadget on\n"); + dev_dbg(otg->phy->dev, "gadget on\n"); /* * Some boards have a switch cotrolled by gpio * to enable/disable internal HUB. Disable internal @@ -1164,20 +1365,20 @@ static void msm_otg_start_peripheral(struct usb_phy *phy, int on) ret = msm_bus_scale_client_update_request( motg->bus_perf_client, 1); if (ret) - dev_err(motg->otg.dev, "%s: Failed to vote for " + dev_err(motg->phy.dev, "%s: Failed to vote for " "bus bandwidth %d\n", __func__, ret); } - usb_gadget_vbus_connect(phy->otg->gadget); + usb_gadget_vbus_connect(otg->gadget); } else { - dev_dbg(phy->dev, "gadget off\n"); - usb_gadget_vbus_disconnect(phy->otg->gadget); + dev_dbg(otg->phy->dev, "gadget off\n"); + usb_gadget_vbus_disconnect(otg->gadget); otg_pm_qos_update_latency(motg, 0); /* Configure BUS performance parameters to default */ if (motg->bus_perf_client) { ret = msm_bus_scale_client_update_request( motg->bus_perf_client, 0); if (ret) - dev_err(motg->otg.dev, "%s: Failed to devote " + dev_err(motg->phy.dev, "%s: Failed to devote " "for bus bw %d\n", __func__, ret); } if (pdata->setup_gpio) @@ -1203,10 +1404,10 @@ static int msm_otg_set_peripheral(struct usb_otg *otg, if (!gadget) { if (otg->phy->state == OTG_STATE_B_PERIPHERAL) { pm_runtime_get_sync(otg->phy->dev); - msm_otg_start_peripheral(otg->phy, 0); + msm_otg_start_peripheral(otg, 0); otg->gadget = NULL; otg->phy->state = OTG_STATE_UNDEFINED; - schedule_work(&motg->sm_work); + queue_work(system_nrt_wq, &motg->sm_work); } else { otg->gadget = NULL; } @@ -1222,7 +1423,7 @@ static int msm_otg_set_peripheral(struct usb_otg *otg, */ if (motg->pdata->mode == USB_PERIPHERAL || otg->host) { pm_runtime_get_sync(otg->phy->dev); - schedule_work(&motg->sm_work); + queue_work(system_nrt_wq, &motg->sm_work); } return 0; @@ -1393,11 +1594,15 @@ static void msm_otg_id_timer_func(unsigned long data) return; } + if (motg->phy.state == OTG_STATE_A_SUSPEND) + goto out; + if (msm_chg_check_aca_intr(motg)) { dev_dbg(motg->phy.dev, "timer: aca work\n"); - schedule_work(&motg->sm_work); + queue_work(system_nrt_wq, &motg->sm_work); } +out: if (!test_bit(ID, &motg->inputs) || test_bit(ID_A, &motg->inputs)) mod_timer(&motg->id_timer, ID_TIMER_FREQ); } @@ -1738,16 +1943,22 @@ static void msm_chg_detect_work(struct work_struct *w) case USB_CHG_STATE_DETECTED: msm_chg_block_off(motg); msm_chg_enable_aca_det(motg); + /* + * Spurious interrupt is seen after enabling ACA detection + * due to which charger detection fails in case of PET. + * Add delay of 100 microsec to avoid that. + */ + udelay(100); msm_chg_enable_aca_intr(motg); dev_dbg(phy->dev, "chg_type = %s\n", chg_to_string(motg->chg_type)); - schedule_work(&motg->sm_work); + queue_work(system_nrt_wq, &motg->sm_work); return; default: return; } - schedule_delayed_work(&motg->chg_work, delay); + queue_delayed_work(system_nrt_wq, &motg->chg_work, delay); } /* @@ -1775,10 +1986,12 @@ static void msm_otg_init_sm(struct msm_otg *motg) clear_bit(B_SESS_VLD, &motg->inputs); } } else if (pdata->otg_control == OTG_PHY_CONTROL) { - if (otgsc & OTGSC_ID) + if (otgsc & OTGSC_ID) { set_bit(ID, &motg->inputs); - else + } else { clear_bit(ID, &motg->inputs); + set_bit(A_BUS_REQ, &motg->inputs); + } if (otgsc & OTGSC_BSV) set_bit(B_SESS_VLD, &motg->inputs); else @@ -1824,11 +2037,12 @@ static void msm_otg_sm_work(struct work_struct *w) { struct msm_otg *motg = container_of(w, struct msm_otg, sm_work); struct usb_otg *otg = motg->phy.otg; + bool work = 0, srp_reqd; pm_runtime_resume(otg->phy->dev); + pr_debug("%s work\n", otg_state_string(otg->phy->state)); switch (otg->phy->state) { case OTG_STATE_UNDEFINED: - dev_dbg(otg->phy->dev, "OTG_STATE_UNDEFINED state\n"); msm_otg_reset(otg->phy); msm_otg_init_sm(motg); otg->phy->state = OTG_STATE_B_IDLE; @@ -1840,31 +2054,15 @@ static void msm_otg_sm_work(struct work_struct *w) } /* FALL THROUGH */ case OTG_STATE_B_IDLE: - dev_dbg(otg->phy->dev, "OTG_STATE_B_IDLE state\n"); if ((!test_bit(ID, &motg->inputs) || test_bit(ID_A, &motg->inputs)) && otg->host) { - if (motg->chg_type == USB_ACA_DOCK_CHARGER) - msm_otg_notify_charger(motg, - IDEV_ACA_CHG_MAX); - else if (test_bit(ID_A, &motg->inputs)) - msm_otg_notify_charger(motg, - IDEV_ACA_CHG_MAX - IUNIT); - else - msm_hsusb_vbus_power(motg, 1); - msm_otg_start_host(otg->phy, 1); - /* - * Link can not generate PHY_ALT interrupt - * in host mode when no device is attached - * to the port. It is also observed PHY_ALT - * interrupt missing upon Micro-A cable disconnect. - * Hence disable PHY_ALT interrupt and perform - * polling to detect RID change. - */ - msm_chg_enable_aca_det(motg); - msm_chg_disable_aca_intr(motg); - mod_timer(&motg->id_timer, ID_TIMER_FREQ); - otg->phy->state = OTG_STATE_A_HOST; + pr_debug("!id || id_A\n"); + clear_bit(B_BUS_REQ, &motg->inputs); + set_bit(A_BUS_REQ, &motg->inputs); + otg->phy->state = OTG_STATE_A_IDLE; + work = 1; } else if (test_bit(B_SESS_VLD, &motg->inputs)) { + pr_debug("b_sess_vld\n"); switch (motg->chg_state) { case USB_CHG_STATE_UNDEFINED: msm_chg_detect_work(&motg->chg_work.work); @@ -1890,20 +2088,21 @@ static void msm_otg_sm_work(struct work_struct *w) case USB_CDP_CHARGER: msm_otg_notify_charger(motg, IDEV_CHG_MAX); - msm_otg_start_peripheral(otg->phy, 1); - otg->phy->state - = OTG_STATE_B_PERIPHERAL; + msm_otg_start_peripheral(otg, 1); + otg->phy->state = + OTG_STATE_B_PERIPHERAL; break; case USB_ACA_C_CHARGER: msm_otg_notify_charger(motg, IDEV_ACA_CHG_MAX); msm_otg_start_peripheral(otg, 1); - otg->state = OTG_STATE_B_PERIPHERAL; + otg->phy->state = + OTG_STATE_B_PERIPHERAL; break; case USB_SDP_CHARGER: - msm_otg_start_peripheral(otg->phy, 1); - otg->phy->state - = OTG_STATE_B_PERIPHERAL; + msm_otg_start_peripheral(otg, 1); + otg->phy->state = + OTG_STATE_B_PERIPHERAL; break; default: break; @@ -1912,74 +2111,472 @@ static void msm_otg_sm_work(struct work_struct *w) default: break; } + } else if (test_bit(B_BUS_REQ, &motg->inputs)) { + pr_debug("b_sess_end && b_bus_req\n"); + if (msm_otg_start_srp(otg) < 0) { + clear_bit(B_BUS_REQ, &motg->inputs); + work = 1; + break; + } + otg->phy->state = OTG_STATE_B_SRP_INIT; + msm_otg_start_timer(motg, TB_SRP_FAIL, B_SRP_FAIL); + break; } else { + pr_debug("chg_work cancel"); cancel_delayed_work_sync(&motg->chg_work); msm_otg_notify_charger(motg, 0); motg->chg_state = USB_CHG_STATE_UNDEFINED; motg->chg_type = USB_INVALID_CHARGER; - msm_otg_reset(otg); - pm_runtime_put_noidle(otg->dev); - pm_runtime_suspend(otg->dev); + msm_otg_reset(otg->phy); + pm_runtime_put_noidle(otg->phy->dev); + pm_runtime_suspend(otg->phy->dev); + } + break; + case OTG_STATE_B_SRP_INIT: + if (!test_bit(ID, &motg->inputs) || + test_bit(ID_A, &motg->inputs) || + test_bit(ID_C, &motg->inputs) || + (test_bit(B_SESS_VLD, &motg->inputs) && + !test_bit(ID_B, &motg->inputs))) { + pr_debug("!id || id_a/c || b_sess_vld+!id_b\n"); + msm_otg_del_timer(motg); + otg->phy->state = OTG_STATE_B_IDLE; + /* + * clear VBUSVLDEXTSEL and VBUSVLDEXT register + * bits after SRP initiation. + */ + ulpi_write(otg->phy, 0x0, 0x98); + work = 1; + } else if (test_bit(B_SRP_FAIL, &motg->tmouts)) { + pr_debug("b_srp_fail\n"); + pr_info("A-device did not respond to SRP\n"); + clear_bit(B_BUS_REQ, &motg->inputs); + clear_bit(B_SRP_FAIL, &motg->tmouts); + otg_send_event(OTG_EVENT_NO_RESP_FOR_SRP); + ulpi_write(otg->phy, 0x0, 0x98); + otg->phy->state = OTG_STATE_B_IDLE; + motg->b_last_se0_sess = jiffies; + work = 1; } break; case OTG_STATE_B_PERIPHERAL: - dev_dbg(otg->phy->dev, "OTG_STATE_B_PERIPHERAL state\n"); - if (!test_bit(B_SESS_VLD, &motg->inputs) || - !test_bit(ID, &motg->inputs) || - !test_bit(ID_C, &motg->inputs)) { - msm_otg_start_peripheral(otg->phy, 0); + if (!test_bit(ID, &motg->inputs) || + test_bit(ID_A, &motg->inputs) || + test_bit(ID_B, &motg->inputs) || + !test_bit(B_SESS_VLD, &motg->inputs)) { + pr_debug("!id || id_a/b || !b_sess_vld\n"); + msm_otg_notify_charger(motg, 0); + srp_reqd = otg->gadget->otg_srp_reqd; + msm_otg_start_peripheral(otg, 0); + motg->chg_state = USB_CHG_STATE_UNDEFINED; + motg->chg_type = USB_INVALID_CHARGER; + if (test_bit(ID_B, &motg->inputs)) + clear_bit(ID_B, &motg->inputs); + clear_bit(B_BUS_REQ, &motg->inputs); otg->phy->state = OTG_STATE_B_IDLE; - schedule_work(w); + motg->b_last_se0_sess = jiffies; + if (srp_reqd) + msm_otg_start_timer(motg, + TB_TST_SRP, B_TST_SRP); + else + work = 1; + } else if (test_bit(B_BUS_REQ, &motg->inputs) && + otg->gadget->b_hnp_enable && + test_bit(A_BUS_SUSPEND, &motg->inputs)) { + pr_debug("b_bus_req && b_hnp_en && a_bus_suspend\n"); + msm_otg_start_timer(motg, TB_ASE0_BRST, B_ASE0_BRST); + /* D+ pullup should not be disconnected within 4msec + * after A device suspends the bus. Otherwise PET will + * fail the compliance test. + */ + udelay(1000); + msm_otg_start_peripheral(otg, 0); + otg->phy->state = OTG_STATE_B_WAIT_ACON; + /* + * start HCD even before A-device enable + * pull-up to meet HNP timings. + */ + otg->host->is_b_host = 1; + msm_otg_start_host(otg, 1); } else if (test_bit(ID_C, &motg->inputs)) { msm_otg_notify_charger(motg, IDEV_ACA_CHG_MAX); } break; - case OTG_STATE_A_HOST: - dev_dbg(otg->phy->dev, "OTG_STATE_A_HOST state\n"); - if (test_bit(ID, &motg->inputs) && - !test_bit(ID_A, &motg->inputs)) { - msm_otg_start_host(otg->phy, 0); - msm_hsusb_vbus_power(motg, 0); - msleep(100); /* TA_WAIT_VFALL */ + case OTG_STATE_B_WAIT_ACON: + if (!test_bit(ID, &motg->inputs) || + test_bit(ID_A, &motg->inputs) || + test_bit(ID_B, &motg->inputs) || + !test_bit(B_SESS_VLD, &motg->inputs)) { + pr_debug("!id || id_a/b || !b_sess_vld\n"); + msm_otg_del_timer(motg); /* - * Exit point of host mode. - * - * 1. Micro-A cable disconnect: Just schedule - * the work. PHY is reset in B_IDLE and LPM - * is allowed. - * 2. ID_GND --> ID_B: No need to reset the PHY. - * HCD core clears all PORTSC bits and initializes - * the controller to host mode in remove_hcd. - * Restore PORTSC transceiver select bits (ULPI) - * and reset the controller to change MODE bits. - * PHY_ALT interrupt can not occur in host mode. + * A-device is physically disconnected during + * HNP. Remove HCD. */ - del_timer_sync(&motg->id_timer); - if (motg->chg_state != USB_CHG_STATE_UNDEFINED) { - msm_otg_link_reset(motg); - msm_chg_enable_aca_intr(motg); - } + msm_otg_start_host(otg, 0); + otg->host->is_b_host = 0; + + clear_bit(B_BUS_REQ, &motg->inputs); + clear_bit(A_BUS_SUSPEND, &motg->inputs); + motg->b_last_se0_sess = jiffies; otg->phy->state = OTG_STATE_B_IDLE; - schedule_work(w); + msm_otg_reset(otg->phy); + work = 1; + } else if (test_bit(A_CONN, &motg->inputs)) { + pr_debug("a_conn\n"); + clear_bit(A_BUS_SUSPEND, &motg->inputs); + otg->phy->state = OTG_STATE_B_HOST; + /* + * PET disconnects D+ pullup after reset is generated + * by B device in B_HOST role which is not detected by + * B device. As workaorund , start timer of 300msec + * and stop timer if A device is enumerated else clear + * A_CONN. + */ + msm_otg_start_timer(motg, TB_TST_CONFIG, + B_TST_CONFIG); + } else if (test_bit(B_ASE0_BRST, &motg->tmouts)) { + pr_debug("b_ase0_brst_tmout\n"); + pr_info("B HNP fail:No response from A device\n"); + msm_otg_start_host(otg, 0); + msm_otg_reset(otg->phy); + otg->host->is_b_host = 0; + clear_bit(B_ASE0_BRST, &motg->tmouts); + clear_bit(A_BUS_SUSPEND, &motg->inputs); + clear_bit(B_BUS_REQ, &motg->inputs); + otg_send_event(OTG_EVENT_HNP_FAILED); + otg->phy->state = OTG_STATE_B_IDLE; + work = 1; + } else if (test_bit(ID_C, &motg->inputs)) { + msm_otg_notify_charger(motg, IDEV_ACA_CHG_MAX); + } + break; + case OTG_STATE_B_HOST: + if (!test_bit(B_BUS_REQ, &motg->inputs) || + !test_bit(A_CONN, &motg->inputs) || + !test_bit(B_SESS_VLD, &motg->inputs)) { + pr_debug("!b_bus_req || !a_conn || !b_sess_vld\n"); + clear_bit(A_CONN, &motg->inputs); + clear_bit(B_BUS_REQ, &motg->inputs); + msm_otg_start_host(otg, 0); + otg->host->is_b_host = 0; + otg->phy->state = OTG_STATE_B_IDLE; + msm_otg_reset(otg->phy); + work = 1; + } else if (test_bit(ID_C, &motg->inputs)) { + msm_otg_notify_charger(motg, IDEV_ACA_CHG_MAX); + } + break; + case OTG_STATE_A_IDLE: + otg->default_a = 1; + if (test_bit(ID, &motg->inputs) && + !test_bit(ID_A, &motg->inputs)) { + pr_debug("id && !id_a\n"); + otg->default_a = 0; + clear_bit(A_BUS_DROP, &motg->inputs); + otg->phy->state = OTG_STATE_B_IDLE; + del_timer_sync(&motg->id_timer); + msm_otg_link_reset(motg); + msm_chg_enable_aca_intr(motg); + msm_otg_notify_charger(motg, 0); + work = 1; + } else if (!test_bit(A_BUS_DROP, &motg->inputs) && + (test_bit(A_SRP_DET, &motg->inputs) || + test_bit(A_BUS_REQ, &motg->inputs))) { + pr_debug("!a_bus_drop && (a_srp_det || a_bus_req)\n"); + + clear_bit(A_SRP_DET, &motg->inputs); + /* Disable SRP detection */ + writel_relaxed((readl_relaxed(USB_OTGSC) & + ~OTGSC_INTSTS_MASK) & + ~OTGSC_DPIE, USB_OTGSC); + + otg->phy->state = OTG_STATE_A_WAIT_VRISE; + /* VBUS should not be supplied before end of SRP pulse + * generated by PET, if not complaince test fail. + */ + usleep_range(10000, 12000); + /* ACA: ID_A: Stop charging untill enumeration */ + if (test_bit(ID_A, &motg->inputs)) + msm_otg_notify_charger(motg, 0); + else + msm_hsusb_vbus_power(motg, 1); + msm_otg_start_timer(motg, TA_WAIT_VRISE, A_WAIT_VRISE); + } else { + pr_debug("No session requested\n"); + clear_bit(A_BUS_DROP, &motg->inputs); + if (test_bit(ID_A, &motg->inputs)) { + msm_otg_notify_charger(motg, + IDEV_ACA_CHG_MAX); + } else if (!test_bit(ID, &motg->inputs)) { + msm_otg_notify_charger(motg, 0); + /* + * A-device is not providing power on VBUS. + * Enable SRP detection. + */ + writel_relaxed(0x13, USB_USBMODE); + writel_relaxed((readl_relaxed(USB_OTGSC) & + ~OTGSC_INTSTS_MASK) | + OTGSC_DPIE, USB_OTGSC); + mb(); + } + } + break; + case OTG_STATE_A_WAIT_VRISE: + if ((test_bit(ID, &motg->inputs) && + !test_bit(ID_A, &motg->inputs)) || + test_bit(A_BUS_DROP, &motg->inputs) || + test_bit(A_WAIT_VRISE, &motg->tmouts)) { + pr_debug("id || a_bus_drop || a_wait_vrise_tmout\n"); + clear_bit(A_BUS_REQ, &motg->inputs); + msm_otg_del_timer(motg); + msm_hsusb_vbus_power(motg, 0); + otg->phy->state = OTG_STATE_A_WAIT_VFALL; + msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL); + } else if (test_bit(A_VBUS_VLD, &motg->inputs)) { + pr_debug("a_vbus_vld\n"); + otg->phy->state = OTG_STATE_A_WAIT_BCON; + if (TA_WAIT_BCON > 0) + msm_otg_start_timer(motg, TA_WAIT_BCON, + A_WAIT_BCON); + msm_otg_start_host(otg, 1); + msm_chg_enable_aca_det(motg); + msm_chg_disable_aca_intr(motg); + mod_timer(&motg->id_timer, ID_TIMER_INITIAL_FREQ); + if (msm_chg_check_aca_intr(motg)) + work = 1; + } + break; + case OTG_STATE_A_WAIT_BCON: + if ((test_bit(ID, &motg->inputs) && + !test_bit(ID_A, &motg->inputs)) || + test_bit(A_BUS_DROP, &motg->inputs) || + test_bit(A_WAIT_BCON, &motg->tmouts)) { + pr_debug("(id && id_a/b/c) || a_bus_drop ||" + "a_wait_bcon_tmout\n"); + if (test_bit(A_WAIT_BCON, &motg->tmouts)) { + pr_info("Device No Response\n"); + otg_send_event(OTG_EVENT_DEV_CONN_TMOUT); + } + msm_otg_del_timer(motg); + clear_bit(A_BUS_REQ, &motg->inputs); + clear_bit(B_CONN, &motg->inputs); + msm_otg_start_host(otg, 0); + /* + * ACA: ID_A with NO accessory, just the A plug is + * attached to ACA: Use IDCHG_MAX for charging + */ + if (test_bit(ID_A, &motg->inputs)) + msm_otg_notify_charger(motg, IDEV_CHG_MIN); + else + msm_hsusb_vbus_power(motg, 0); + otg->phy->state = OTG_STATE_A_WAIT_VFALL; + msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL); + } else if (!test_bit(A_VBUS_VLD, &motg->inputs)) { + pr_debug("!a_vbus_vld\n"); + clear_bit(B_CONN, &motg->inputs); + msm_otg_del_timer(motg); + msm_otg_start_host(otg, 0); + otg->phy->state = OTG_STATE_A_VBUS_ERR; + msm_otg_reset(otg->phy); + } else if (test_bit(ID_A, &motg->inputs)) { + msm_hsusb_vbus_power(motg, 0); + } else if (!test_bit(A_BUS_REQ, &motg->inputs)) { + /* + * If TA_WAIT_BCON is infinite, we don;t + * turn off VBUS. Enter low power mode. + */ + if (TA_WAIT_BCON < 0) + pm_runtime_put_sync(otg->phy->dev); + } else if (!test_bit(ID, &motg->inputs)) { + msm_hsusb_vbus_power(motg, 1); + } + break; + case OTG_STATE_A_HOST: + if ((test_bit(ID, &motg->inputs) && + !test_bit(ID_A, &motg->inputs)) || + test_bit(A_BUS_DROP, &motg->inputs)) { + pr_debug("id_a/b/c || a_bus_drop\n"); + clear_bit(B_CONN, &motg->inputs); + clear_bit(A_BUS_REQ, &motg->inputs); + msm_otg_del_timer(motg); + otg->phy->state = OTG_STATE_A_WAIT_VFALL; + msm_otg_start_host(otg, 0); + if (!test_bit(ID_A, &motg->inputs)) + msm_hsusb_vbus_power(motg, 0); + msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL); + } else if (!test_bit(A_VBUS_VLD, &motg->inputs)) { + pr_debug("!a_vbus_vld\n"); + clear_bit(B_CONN, &motg->inputs); + msm_otg_del_timer(motg); + otg->phy->state = OTG_STATE_A_VBUS_ERR; + msm_otg_start_host(otg, 0); + msm_otg_reset(otg->phy); + } else if (!test_bit(A_BUS_REQ, &motg->inputs)) { + /* + * a_bus_req is de-asserted when root hub is + * suspended or HNP is in progress. + */ + pr_debug("!a_bus_req\n"); + msm_otg_del_timer(motg); + otg->phy->state = OTG_STATE_A_SUSPEND; + if (otg->host->b_hnp_enable) + msm_otg_start_timer(motg, TA_AIDL_BDIS, + A_AIDL_BDIS); + else + pm_runtime_put_sync(otg->phy->dev); + } else if (!test_bit(B_CONN, &motg->inputs)) { + pr_debug("!b_conn\n"); + msm_otg_del_timer(motg); + otg->phy->state = OTG_STATE_A_WAIT_BCON; + if (TA_WAIT_BCON > 0) + msm_otg_start_timer(motg, TA_WAIT_BCON, + A_WAIT_BCON); + if (msm_chg_check_aca_intr(motg)) + work = 1; + } else if (test_bit(ID_A, &motg->inputs)) { + msm_otg_del_timer(motg); + msm_hsusb_vbus_power(motg, 0); + if (motg->chg_type == USB_ACA_DOCK_CHARGER) + msm_otg_notify_charger(motg, + IDEV_ACA_CHG_MAX); + else + msm_otg_notify_charger(motg, + IDEV_CHG_MIN - motg->mA_port); + } else if (!test_bit(ID, &motg->inputs)) { + motg->chg_state = USB_CHG_STATE_UNDEFINED; + motg->chg_type = USB_INVALID_CHARGER; + msm_otg_notify_charger(motg, 0); + msm_hsusb_vbus_power(motg, 1); + } + break; + case OTG_STATE_A_SUSPEND: + if ((test_bit(ID, &motg->inputs) && + !test_bit(ID_A, &motg->inputs)) || + test_bit(A_BUS_DROP, &motg->inputs) || + test_bit(A_AIDL_BDIS, &motg->tmouts)) { + pr_debug("id_a/b/c || a_bus_drop ||" + "a_aidl_bdis_tmout\n"); + msm_otg_del_timer(motg); + clear_bit(B_CONN, &motg->inputs); + otg->phy->state = OTG_STATE_A_WAIT_VFALL; + msm_otg_start_host(otg, 0); + msm_otg_reset(otg->phy); + if (!test_bit(ID_A, &motg->inputs)) + msm_hsusb_vbus_power(motg, 0); + msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL); + } else if (!test_bit(A_VBUS_VLD, &motg->inputs)) { + pr_debug("!a_vbus_vld\n"); + msm_otg_del_timer(motg); + clear_bit(B_CONN, &motg->inputs); + otg->phy->state = OTG_STATE_A_VBUS_ERR; + msm_otg_start_host(otg, 0); + msm_otg_reset(otg->phy); + } else if (!test_bit(B_CONN, &motg->inputs) && + otg->host->b_hnp_enable) { + pr_debug("!b_conn && b_hnp_enable"); + otg->phy->state = OTG_STATE_A_PERIPHERAL; + msm_otg_host_hnp_enable(otg, 1); + otg->gadget->is_a_peripheral = 1; + msm_otg_start_peripheral(otg, 1); + } else if (!test_bit(B_CONN, &motg->inputs) && + !otg->host->b_hnp_enable) { + pr_debug("!b_conn && !b_hnp_enable"); + /* + * bus request is dropped during suspend. + * acquire again for next device. + */ + set_bit(A_BUS_REQ, &motg->inputs); + otg->phy->state = OTG_STATE_A_WAIT_BCON; + if (TA_WAIT_BCON > 0) + msm_otg_start_timer(motg, TA_WAIT_BCON, + A_WAIT_BCON); } else if (test_bit(ID_A, &motg->inputs)) { msm_hsusb_vbus_power(motg, 0); msm_otg_notify_charger(motg, - IDEV_ACA_CHG_MAX - motg->mA_port); + IDEV_CHG_MIN - motg->mA_port); } else if (!test_bit(ID, &motg->inputs)) { msm_otg_notify_charger(motg, 0); msm_hsusb_vbus_power(motg, 1); } break; + case OTG_STATE_A_PERIPHERAL: + if ((test_bit(ID, &motg->inputs) && + !test_bit(ID_A, &motg->inputs)) || + test_bit(A_BUS_DROP, &motg->inputs)) { + pr_debug("id _f/b/c || a_bus_drop\n"); + /* Clear BIDL_ADIS timer */ + msm_otg_del_timer(motg); + otg->phy->state = OTG_STATE_A_WAIT_VFALL; + msm_otg_start_peripheral(otg, 0); + otg->gadget->is_a_peripheral = 0; + msm_otg_start_host(otg, 0); + msm_otg_reset(otg->phy); + if (!test_bit(ID_A, &motg->inputs)) + msm_hsusb_vbus_power(motg, 0); + msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL); + } else if (!test_bit(A_VBUS_VLD, &motg->inputs)) { + pr_debug("!a_vbus_vld\n"); + /* Clear BIDL_ADIS timer */ + msm_otg_del_timer(motg); + otg->phy->state = OTG_STATE_A_VBUS_ERR; + msm_otg_start_peripheral(otg, 0); + otg->gadget->is_a_peripheral = 0; + msm_otg_start_host(otg, 0); + } else if (test_bit(A_BIDL_ADIS, &motg->tmouts)) { + pr_debug("a_bidl_adis_tmout\n"); + msm_otg_start_peripheral(otg, 0); + otg->gadget->is_a_peripheral = 0; + otg->phy->state = OTG_STATE_A_WAIT_BCON; + set_bit(A_BUS_REQ, &motg->inputs); + msm_otg_host_hnp_enable(otg, 0); + if (TA_WAIT_BCON > 0) + msm_otg_start_timer(motg, TA_WAIT_BCON, + A_WAIT_BCON); + } else if (test_bit(ID_A, &motg->inputs)) { + msm_hsusb_vbus_power(motg, 0); + msm_otg_notify_charger(motg, + IDEV_CHG_MIN - motg->mA_port); + } else if (!test_bit(ID, &motg->inputs)) { + msm_otg_notify_charger(motg, 0); + msm_hsusb_vbus_power(motg, 1); + } + break; + case OTG_STATE_A_WAIT_VFALL: + if (test_bit(A_WAIT_VFALL, &motg->tmouts)) { + clear_bit(A_VBUS_VLD, &motg->inputs); + otg->phy->state = OTG_STATE_A_IDLE; + work = 1; + } + break; + case OTG_STATE_A_VBUS_ERR: + if ((test_bit(ID, &motg->inputs) && + !test_bit(ID_A, &motg->inputs)) || + test_bit(A_BUS_DROP, &motg->inputs) || + test_bit(A_CLR_ERR, &motg->inputs)) { + otg->phy->state = OTG_STATE_A_WAIT_VFALL; + if (!test_bit(ID_A, &motg->inputs)) + msm_hsusb_vbus_power(motg, 0); + msm_otg_start_timer(motg, TA_WAIT_VFALL, A_WAIT_VFALL); + motg->chg_state = USB_CHG_STATE_UNDEFINED; + motg->chg_type = USB_INVALID_CHARGER; + msm_otg_notify_charger(motg, 0); + } + break; default: break; } + if (work) + queue_work(system_nrt_wq, &motg->sm_work); } static irqreturn_t msm_otg_irq(int irq, void *data) { struct msm_otg *motg = data; - struct usb_phy *phy = &motg->phy; - u32 otgsc = 0, usbsts; + struct usb_otg *otg = motg->phy.otg; + u32 otgsc = 0, usbsts, pc; + bool work = 0; + irqreturn_t ret = IRQ_HANDLED; if (atomic_read(&motg->in_lpm)) { pr_debug("OTG IRQ: in LPM\n"); @@ -1990,44 +2587,144 @@ static irqreturn_t msm_otg_irq(int irq, void *data) } usbsts = readl(USB_USBSTS); - if ((usbsts & PHY_ALT_INT)) { - dev_dbg(phy->dev, "PHY_ALT interrupt\n"); - writel(PHY_ALT_INT, USB_USBSTS); - if (msm_chg_check_aca_intr(motg)) { - dev_dbg(phy->dev, "ACA work from IRQ\n"); - schedule_work(&motg->sm_work); - } - return IRQ_HANDLED; - } - otgsc = readl(USB_OTGSC); - if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS))) + + if (!(otgsc & OTG_OTGSTS_MASK) && !(usbsts & OTG_USBSTS_MASK)) return IRQ_NONE; if ((otgsc & OTGSC_IDIS) && (otgsc & OTGSC_IDIE)) { if (otgsc & OTGSC_ID) { - dev_dbg(phy->dev, "ID set\n"); + dev_dbg(otg->phy->dev, "ID set\n"); set_bit(ID, &motg->inputs); } else { - dev_dbg(phy->dev, "ID clear\n"); + dev_dbg(otg->phy->dev, "ID clear\n"); + /* + * Assert a_bus_req to supply power on + * VBUS when Micro/Mini-A cable is connected + * with out user intervention. + */ + set_bit(A_BUS_REQ, &motg->inputs); clear_bit(ID, &motg->inputs); msm_chg_enable_aca_det(motg); } - schedule_work(&motg->sm_work); - } else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) { + writel_relaxed(otgsc, USB_OTGSC); + work = 1; + } else if (otgsc & OTGSC_DPIS) { + pr_debug("DPIS detected\n"); + writel_relaxed(otgsc, USB_OTGSC); + set_bit(A_SRP_DET, &motg->inputs); + set_bit(A_BUS_REQ, &motg->inputs); + work = 1; + } else if (otgsc & OTGSC_BSVIS) { + writel_relaxed(otgsc, USB_OTGSC); + /* + * BSV interrupt comes when operating as an A-device + * (VBUS on/off). + * But, handle BSV when charger is removed from ACA in ID_A + */ + if ((otg->phy->state >= OTG_STATE_A_IDLE) && + !test_bit(ID_A, &motg->inputs)) + return IRQ_HANDLED; if (otgsc & OTGSC_BSV) { - dev_dbg(phy->dev, "BSV set\n"); + dev_dbg(otg->phy->dev, "BSV set\n"); set_bit(B_SESS_VLD, &motg->inputs); } else { - dev_dbg(phy->dev, "BSV clear\n"); + dev_dbg(otg->phy->dev, "BSV clear\n"); clear_bit(B_SESS_VLD, &motg->inputs); msm_chg_check_aca_intr(motg); } - schedule_work(&motg->sm_work); + work = 1; + } else if (usbsts & STS_PCI) { + pc = readl_relaxed(USB_PORTSC); + pr_debug("portsc = %x\n", pc); + ret = IRQ_NONE; + /* + * HCD Acks PCI interrupt. We use this to switch + * between different OTG states. + */ + work = 1; + switch (otg->phy->state) { + case OTG_STATE_A_SUSPEND: + if (otg->host->b_hnp_enable && (pc & PORTSC_CSC) && + !(pc & PORTSC_CCS)) { + pr_debug("B_CONN clear\n"); + clear_bit(B_CONN, &motg->inputs); + msm_otg_del_timer(motg); + } + break; + case OTG_STATE_A_PERIPHERAL: + /* + * A-peripheral observed activity on bus. + * clear A_BIDL_ADIS timer. + */ + msm_otg_del_timer(motg); + work = 0; + break; + case OTG_STATE_B_WAIT_ACON: + if ((pc & PORTSC_CSC) && (pc & PORTSC_CCS)) { + pr_debug("A_CONN set\n"); + set_bit(A_CONN, &motg->inputs); + /* Clear ASE0_BRST timer */ + msm_otg_del_timer(motg); + } + break; + case OTG_STATE_B_HOST: + if ((pc & PORTSC_CSC) && !(pc & PORTSC_CCS)) { + pr_debug("A_CONN clear\n"); + clear_bit(A_CONN, &motg->inputs); + msm_otg_del_timer(motg); + } + break; + case OTG_STATE_A_WAIT_BCON: + if (TA_WAIT_BCON < 0) + set_bit(A_BUS_REQ, &motg->inputs); + default: + work = 0; + break; + } + } else if (usbsts & STS_URI) { + ret = IRQ_NONE; + switch (otg->phy->state) { + case OTG_STATE_A_PERIPHERAL: + /* + * A-peripheral observed activity on bus. + * clear A_BIDL_ADIS timer. + */ + msm_otg_del_timer(motg); + work = 0; + break; + default: + work = 0; + break; + } + } else if (usbsts & STS_SLI) { + ret = IRQ_NONE; + work = 0; + switch (otg->phy->state) { + case OTG_STATE_B_PERIPHERAL: + if (otg->gadget->b_hnp_enable) { + set_bit(A_BUS_SUSPEND, &motg->inputs); + set_bit(B_BUS_REQ, &motg->inputs); + work = 1; + } + break; + case OTG_STATE_A_PERIPHERAL: + msm_otg_start_timer(motg, TA_BIDL_ADIS, + A_BIDL_ADIS); + break; + default: + break; + } + } else if ((usbsts & PHY_ALT_INT)) { + writel_relaxed(PHY_ALT_INT, USB_USBSTS); + if (msm_chg_check_aca_intr(motg)) + work = 1; + ret = IRQ_HANDLED; } + if (work) + queue_work(system_nrt_wq, &motg->sm_work); - writel(otgsc, USB_OTGSC); - return IRQ_HANDLED; + return ret; } static void msm_otg_set_vbus_state(int online) @@ -2050,7 +2747,7 @@ static void msm_otg_set_vbus_state(int online) return; } - schedule_work(&motg->sm_work); + queue_work(system_nrt_wq, &motg->sm_work); } static irqreturn_t msm_pmic_id_irq(int irq, void *data) @@ -2066,10 +2763,11 @@ static irqreturn_t msm_pmic_id_irq(int irq, void *data) } else { pr_debug("PMIC: ID clear\n"); clear_bit(ID, &motg->inputs); + set_bit(A_BUS_REQ, &motg->inputs); } if (motg->phy.state != OTG_STATE_UNDEFINED) - schedule_work(&motg->sm_work); + queue_work(system_nrt_wq, &motg->sm_work); return IRQ_HANDLED; } @@ -2105,7 +2803,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, struct seq_file *s = file->private_data; struct msm_otg *motg = s->private; char buf[16]; - struct usb_otg *otg = motg->phy.otg; + struct usb_phy *phy = &motg->phy; int status = count; enum usb_mode_type req_mode; @@ -2129,7 +2827,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, switch (req_mode) { case USB_NONE: - switch (otg->phy->state) { + switch (phy->state) { case OTG_STATE_A_HOST: case OTG_STATE_B_PERIPHERAL: set_bit(ID, &motg->inputs); @@ -2140,7 +2838,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, } break; case USB_PERIPHERAL: - switch (otg->phy->state) { + switch (phy->state) { case OTG_STATE_B_IDLE: case OTG_STATE_A_HOST: set_bit(ID, &motg->inputs); @@ -2151,7 +2849,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, } break; case USB_HOST: - switch (otg->phy->state) { + switch (phy->state) { case OTG_STATE_B_IDLE: case OTG_STATE_B_PERIPHERAL: clear_bit(ID, &motg->inputs); @@ -2165,7 +2863,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, } pm_runtime_resume(phy->dev); - schedule_work(&motg->sm_work); + queue_work(system_nrt_wq, &motg->sm_work); out: return status; } @@ -2634,6 +3332,7 @@ static int __init msm_otg_probe(struct platform_device *pdev) mb(); wake_lock_init(&motg->wlock, WAKE_LOCK_SUSPEND, "msm_otg"); + msm_otg_init_timer(motg); INIT_WORK(&motg->sm_work, msm_otg_sm_work); INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work); setup_timer(&motg->id_timer, msm_otg_id_timer_func, @@ -2654,6 +3353,8 @@ static int __init msm_otg_probe(struct platform_device *pdev) phy->otg->phy = &motg->phy; phy->otg->set_host = msm_otg_set_host; phy->otg->set_peripheral = msm_otg_set_peripheral; + phy->otg->start_hnp = msm_otg_start_hnp; + phy->otg->start_srp = msm_otg_start_srp; ret = usb_set_transceiver(&motg->phy); if (ret) { diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 8732b1b0bce..2ddf1a64ef8 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -187,6 +187,46 @@ struct msm_otg_platform_data { struct msm_bus_scale_pdata *bus_scale_table; }; +/* Timeout (in msec) values (min - max) associated with OTG timers */ + +#define TA_WAIT_VRISE 100 /* ( - 100) */ +#define TA_WAIT_VFALL 500 /* ( - 1000) */ + +/* + * This option is set for embedded hosts or OTG devices in which leakage + * currents are very minimal. + */ +#ifdef CONFIG_USB_OTG +#define TA_WAIT_BCON 30000 /* (1100 - 30000) */ +#else +#define TA_WAIT_BCON -1 +#endif + +#define TA_AIDL_BDIS 500 /* (200 - ) */ +#define TA_BIDL_ADIS 155 /* (155 - 200) */ +#define TB_SRP_FAIL 6000 /* (5000 - 6000) */ +#define TB_ASE0_BRST 200 /* (155 - ) */ + +/* TB_SSEND_SRP and TB_SE0_SRP are combined */ +#define TB_SRP_INIT 2000 /* (1500 - ) */ + +#define TA_TST_MAINT 10100 /* (9900 - 10100) */ +#define TB_TST_SRP 3000 /* ( - 5000) */ +#define TB_TST_CONFIG 300 + +/* Timeout variables */ + +#define A_WAIT_VRISE 0 +#define A_WAIT_VFALL 1 +#define A_WAIT_BCON 2 +#define A_AIDL_BDIS 3 +#define A_BIDL_ADIS 4 +#define B_SRP_FAIL 5 +#define B_ASE0_BRST 6 +#define A_TST_MAINT 7 +#define B_TST_SRP 8 +#define B_TST_CONFIG 9 + /** * struct msm_otg: OTG driver data. Shared by HCD and DCD. * @otg: USB OTG Transceiver structure. @@ -233,6 +273,18 @@ struct msm_otg { #define ID_A 2 #define ID_B 3 #define ID_C 4 +#define A_BUS_DROP 5 +#define A_BUS_REQ 6 +#define A_SRP_DET 7 +#define A_VBUS_VLD 8 +#define B_CONN 9 +#define ADP_CHANGE 10 +#define POWER_UP 11 +#define A_CLR_ERR 12 +#define A_BUS_RESUME 13 +#define A_BUS_SUSPEND 14 +#define A_CONN 15 +#define B_BUS_REQ 16 unsigned long inputs; struct work_struct sm_work; atomic_t in_lpm; @@ -273,6 +325,10 @@ struct msm_otg { #define PHY_OTG_COMP_DISABLED BIT(2) struct pm_qos_request_list pm_qos_req_dma; int reset_counter; + unsigned long b_last_se0_sess; + unsigned long tmouts; + u8 active_tmout; + struct hrtimer timer; }; struct msm_hsic_host_platform_data { diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h index fcd91daab9b..8a05136f4dd 100644 --- a/include/linux/usb/msm_hsusb_hw.h +++ b/include/linux/usb/msm_hsusb_hw.h @@ -33,6 +33,8 @@ #define PORTSC_PHCD (1 << 23) /* phy suspend mode */ #define PORTSC_PTS_MASK (3 << 30) #define PORTSC_PTS_ULPI (3 << 30) +#define PORTSC_CSC (1 << 1) +#define PORTSC_CCS (1 << 0) #define USB_ULPI_VIEWPORT (MSM_USB_BASE + 0x0170) #define ULPI_RUN (1 << 30) @@ -54,14 +56,26 @@ #define PHY_IDHV_INTEN (1 << 8) /* PHY ID HV interrupt */ #define PHY_OTGSESSVLDHV_INTEN (1 << 9) /* PHY Session Valid HV int. */ +#define STS_PCI (1 << 2) /* R/WC - Port Change Detect */ +#define STS_URI (1 << 6) /* R/WC - RESET recv'd */ +#define STS_SLI (1 << 8) /* R/WC - suspend state entered */ + /* OTG definitions */ #define OTGSC_INTSTS_MASK (0x7f << 16) #define OTGSC_IDPU (1 << 5) +#define OTGSC_INTR_MASK (0x7f << 24) +#define OTGSC_HADP (1 << 6) #define OTGSC_ID (1 << 8) #define OTGSC_BSV (1 << 11) #define OTGSC_IDIS (1 << 16) #define OTGSC_BSVIS (1 << 19) #define OTGSC_IDIE (1 << 24) #define OTGSC_BSVIE (1 << 27) +#define OTGSC_DPIE (1 << 30) +#define OTGSC_DPIS (1 << 22) + +/* OTG interrupt status mask */ +#define OTG_USBSTS_MASK (STS_PCI | STS_URI | STS_SLI | PHY_ALT_INT) +#define OTG_OTGSTS_MASK (OTGSC_IDIS | OTGSC_BSVIS | OTGSC_DPIS) #endif /* __LINUX_USB_GADGET_MSM72K_UDC_H__ */