static int msm_otg_set_suspend(struct otg_transceiver *xceiv, int suspend) { struct msm_otg *dev = container_of(xceiv, struct msm_otg, otg); if (!dev || (dev != the_msm_otg)) return -ENODEV; if (suspend) msm_otg_suspend(dev); else { unsigned long timeout; disable_irq(dev->irq); msm_otg_resume(dev); if (!is_phy_clk_disabled()) goto out; timeout = jiffies + msecs_to_jiffies(500); enable_phy_clk(); while (is_phy_clk_disabled()) { if (time_after(jiffies, timeout)) { pr_err("%s: Unable to wakeup phy\n", __func__); otg_reset(dev); break; } msleep(1); } out: enable_irq(dev->irq); } return 0; }
static int msm_otg_suspend(struct msm_otg *dev) { unsigned long timeout; int vbus = 0; disable_irq(dev->irq); if (dev->in_lpm) goto out; otg_reset(dev); ulpi_read(dev, 0x14);/* clear PHY interrupt latch register */ ulpi_write(dev, 0x01, 0x30);/* PHY comparators on in LPM */ ulpi_write(dev, 0x08, 0x09);/* turn off PLL on integrated phy */ timeout = jiffies + msecs_to_jiffies(500); disable_phy_clk(); while (!is_phy_clk_disabled()) { if (time_after(jiffies, timeout)) { pr_err("%s: Unable to suspend phy\n", __func__); otg_reset(dev); goto out; } msleep(1); } writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); clk_disable(dev->clk); clk_disable(dev->pclk); if (device_may_wakeup(dev->otg.dev)) enable_irq_wake(dev->irq); dev->in_lpm = 1; /* TBD: as there is no bus suspend implemented as of now * it should be dummy check */ if (!vbus || release_wlocks) wake_unlock(&dev->wlock); pr_info("%s: usb in low power mode\n", __func__); out: enable_irq(dev->irq); return 0; }
static int msm_otg_suspend(struct msm_otg *dev) { unsigned long timeout; int vbus = 0; disable_irq(dev->irq); if (atomic_read(&dev->in_lpm)) goto out; ulpi_read(dev, 0x14);/* clear PHY interrupt latch register */ /* If there is no pmic notify support turn on phy comparators. */ if (!dev->pmic_notif_supp) ulpi_write(dev, 0x01, 0x30); ulpi_write(dev, 0x08, 0x09);/* turn off PLL on integrated phy */ timeout = jiffies + msecs_to_jiffies(500); disable_phy_clk(); while (!is_phy_clk_disabled()) { if (time_after(jiffies, timeout)) { pr_err("%s: Unable to suspend phy\n", __func__); /* Reset both phy and link */ otg_reset(dev, 1); goto out; } msleep(1); } writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); if (dev->hs_pclk) clk_disable(dev->hs_pclk); if (dev->hs_cclk) clk_disable(dev->hs_cclk); if (device_may_wakeup(dev->otg.dev)) { enable_irq_wake(dev->irq); if (dev->vbus_on_irq) enable_irq_wake(dev->vbus_on_irq); } atomic_set(&dev->in_lpm, 1); if (!vbus && dev->pmic_notif_supp) dev->pdata->pmic_enable_ldo(0); pr_info("%s: usb in low power mode\n", __func__); out: enable_irq(dev->irq); /* TBD: as there is no bus suspend implemented as of now * it should be dummy check */ return 0; }
static int msm_otg_set_suspend(struct otg_transceiver *xceiv, int suspend) { struct msm_otg *dev = container_of(xceiv, struct msm_otg, otg); enum usb_otg_state state; unsigned long flags; if (!dev || (dev != the_msm_otg)) return -ENODEV; spin_lock_irqsave(&dev->lock, flags); state = dev->otg.state; spin_unlock_irqrestore(&dev->lock, flags); pr_debug("suspend request in state: %s\n", state_string(state)); if (suspend) { switch (state) { case OTG_STATE_A_HOST: clear_bit(A_BUS_REQ, &dev->inputs); wake_lock(&dev->wlock); queue_work(dev->wq, &dev->sm_work); break; case OTG_STATE_B_PERIPHERAL: if (xceiv->gadget->b_hnp_enable) { set_bit(A_BUS_SUSPEND, &dev->inputs); set_bit(B_BUS_REQ, &dev->inputs); wake_lock(&dev->wlock); queue_work(dev->wq, &dev->sm_work); } break; case OTG_STATE_A_PERIPHERAL: msm_otg_start_timer(dev, TA_BIDL_ADIS, A_BIDL_ADIS); break; default: break; } } else { unsigned long timeout; switch (state) { case OTG_STATE_A_PERIPHERAL: /* A-peripheral observed activity on bus. * clear A_BIDL_ADIS timer. */ msm_otg_del_timer(dev); break; case OTG_STATE_A_SUSPEND: /* Remote wakeup or resume */ set_bit(A_BUS_REQ, &dev->inputs); spin_lock_irqsave(&dev->lock, flags); dev->otg.state = OTG_STATE_A_HOST; spin_unlock_irqrestore(&dev->lock, flags); break; default: break; } if (suspend == atomic_read(&dev->in_lpm)) return 0; disable_irq(dev->irq); if (dev->pmic_notif_supp) dev->pdata->pmic_enable_ldo(1); msm_otg_resume(dev); if (!is_phy_clk_disabled()) goto out; timeout = jiffies + usecs_to_jiffies(100); enable_phy_clk(); while (is_phy_clk_disabled()) { if (time_after(jiffies, timeout)) { pr_err("%s: Unable to wakeup phy\n", __func__); /* Reset both phy and link */ otg_reset(dev, 1); break; } udelay(10); } out: enable_irq(dev->irq); } return 0; }
static int __init msm_otg_probe(struct platform_device *pdev) { int ret = 0; int vbus_on_irq = 0; struct resource *res; struct msm_otg *dev; dev = kzalloc(sizeof(struct msm_otg), GFP_KERNEL); if (!dev) return -ENOMEM; dev->otg.dev = &pdev->dev; dev->pdata = pdev->dev.platform_data; if (!dev->pdata) { ret = -ENODEV; goto free_dev; } #ifdef CONFIG_USB_EHCI_MSM if (!dev->pdata->vbus_power) { ret = -ENODEV; goto free_dev; } #endif if (dev->pdata->pmic_vbus_irq) { vbus_on_irq = platform_get_irq_byname(pdev, "vbus_on"); if (vbus_on_irq < 0) { pr_err("%s: unable to get vbus on irq\n", __func__); ret = vbus_on_irq; goto free_dev; } } if (dev->pdata->rpc_connect) { ret = dev->pdata->rpc_connect(1); pr_info("%s: rpc_connect(%d)\n", __func__, ret); if (ret) { pr_err("%s: rpc connect failed\n", __func__); ret = -ENODEV; goto free_dev; } } dev->hs_clk = clk_get(&pdev->dev, "usb_hs_clk"); if (IS_ERR(dev->hs_clk)) { pr_err("%s: failed to get usb_hs_clk\n", __func__); ret = PTR_ERR(dev->hs_clk); goto rpc_fail; } clk_set_rate(dev->hs_clk, 60000000); if (!dev->pdata->usb_in_sps) { dev->hs_pclk = clk_get(&pdev->dev, "usb_hs_pclk"); if (IS_ERR(dev->hs_pclk)) { pr_err("%s: failed to get usb_hs_pclk\n", __func__); ret = PTR_ERR(dev->hs_pclk); goto put_hs_clk; } } if (dev->pdata->core_clk) { dev->hs_cclk = clk_get(&pdev->dev, "usb_hs_core_clk"); if (IS_ERR(dev->hs_cclk)) { pr_err("%s: failed to get usb_hs_core_clk\n", __func__); ret = PTR_ERR(dev->hs_cclk); goto put_hs_pclk; } } if (!dev->pdata->phy_reset) { dev->phy_reset_clk = clk_get(&pdev->dev, "usb_phy_clk"); if (IS_ERR(dev->phy_reset_clk)) { pr_err("%s: failed to get usb_phy_clk\n", __func__); ret = PTR_ERR(dev->phy_reset_clk); goto put_hs_cclk; } } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { pr_err("%s: failed to get platform resource mem\n", __func__); ret = -ENODEV; goto put_phy_clk; } dev->regs = ioremap(res->start, resource_size(res)); if (!dev->regs) { pr_err("%s: ioremap failed\n", __func__); ret = -ENOMEM; goto put_phy_clk; } dev->irq = platform_get_irq(pdev, 0); if (!dev->irq) { pr_err("%s: platform_get_irq failed\n", __func__); ret = -ENODEV; goto free_regs; } msm_otg_init_timer(dev); INIT_WORK(&dev->sm_work, msm_otg_sm_work); spin_lock_init(&dev->lock); wake_lock_init(&dev->wlock, WAKE_LOCK_SUSPEND, "msm_otg"); dev->wq = create_singlethread_workqueue("k_otg"); if (!dev->wq) { ret = -ENOMEM; goto free_wlock; } /* enable clocks */ if (dev->hs_pclk) clk_enable(dev->hs_pclk); if (dev->hs_cclk) clk_enable(dev->hs_cclk); /* To reduce phy power consumption and to avoid external LDO * on the board, PMIC comparators can be used to detect VBUS * session change. */ if (dev->pdata->pmic_notif_init) { ret = dev->pdata->pmic_notif_init(); if (!ret) { dev->pmic_notif_supp = 1; dev->pdata->pmic_enable_ldo(1); } else if (ret != -ENOTSUPP) { if (dev->hs_pclk) clk_disable(dev->hs_pclk); if (dev->hs_cclk) clk_disable(dev->hs_cclk); goto free_wq; } } /* Reset both phy and link */ otg_reset(dev, 1); /* ACk all pending interrupts and clear interrupt enable registers */ writel((readl(USB_OTGSC) & ~OTGSC_INTR_MASK), USB_OTGSC); writel(readl(USB_USBSTS), USB_USBSTS); writel(0, USB_USBINTR); ret = request_irq(dev->irq, msm_otg_irq, IRQF_SHARED, "msm_otg", dev); if (ret) { pr_info("%s: request irq failed\n", __func__); if (dev->hs_pclk) clk_disable(dev->hs_pclk); if (dev->hs_cclk) clk_disable(dev->hs_cclk); goto free_wq; } the_msm_otg = dev; dev->vbus_on_irq = vbus_on_irq; dev->otg.set_peripheral = msm_otg_set_peripheral; #ifdef CONFIG_USB_EHCI_MSM dev->otg.set_host = msm_otg_set_host; #endif dev->otg.set_suspend = msm_otg_set_suspend; dev->otg.start_hnp = msm_otg_start_hnp; dev->set_clk = msm_otg_set_clk; if (otg_set_transceiver(&dev->otg)) { WARN_ON(1); goto free_otg_irq; } device_init_wakeup(&pdev->dev, 1); if (vbus_on_irq) { ret = request_irq(vbus_on_irq, pmic_vbus_on_irq, IRQF_TRIGGER_RISING, "msm_otg_vbus_on", NULL); if (ret) { pr_info("%s: request_irq for vbus_on" "interrupt failed\n", __func__); goto free_otg_irq; } } #ifdef CONFIG_DEBUG_FS ret = otg_debugfs_init(dev); if (ret) { pr_info("%s: otg_debugfs_init failed\n", __func__); goto free_vbus_irq; } #endif ret = sysfs_create_group(&pdev->dev.kobj, &msm_otg_attr_grp); if (ret < 0) { pr_err("%s: Failed to create the sysfs entry \n", __func__); #ifdef CONFIG_DEBUG_FS otg_debugfs_cleanup(); #endif goto free_vbus_irq; } return 0; free_vbus_irq: if (vbus_on_irq) free_irq(vbus_on_irq, 0); free_otg_irq: free_irq(dev->irq, dev); free_wq: destroy_workqueue(dev->wq); free_wlock: wake_lock_destroy(&dev->wlock); free_regs: iounmap(dev->regs); put_phy_clk: if (dev->phy_reset_clk) clk_put(dev->phy_reset_clk); put_hs_cclk: if (dev->hs_cclk) clk_put(dev->hs_cclk); put_hs_pclk: if (dev->hs_pclk) clk_put(dev->hs_pclk); put_hs_clk: if (dev->hs_clk) clk_put(dev->hs_clk); rpc_fail: dev->pdata->rpc_connect(0); free_dev: kfree(dev); return ret; }
static void msm_otg_sm_work(struct work_struct *w) { struct msm_otg *dev = container_of(w, struct msm_otg, sm_work); int ret; int work = 0; enum usb_otg_state state; if (atomic_read(&dev->in_lpm)) msm_otg_set_suspend(&dev->otg, 0); spin_lock_irq(&dev->lock); state = dev->otg.state; spin_unlock_irq(&dev->lock); pr_debug("state: %s\n", state_string(state)); switch (state) { case OTG_STATE_UNDEFINED: if (!dev->otg.host || !is_host()) set_bit(ID, &dev->inputs); if (dev->otg.gadget && is_b_sess_vld()) set_bit(B_SESS_VLD, &dev->inputs); spin_lock_irq(&dev->lock); if (test_bit(ID, &dev->inputs)) { dev->otg.state = OTG_STATE_B_IDLE; } else { set_bit(A_BUS_REQ, &dev->inputs); dev->otg.state = OTG_STATE_A_IDLE; } spin_unlock_irq(&dev->lock); work = 1; break; case OTG_STATE_B_IDLE: dev->otg.default_a = 0; if (!test_bit(ID, &dev->inputs)) { pr_debug("!id\n"); clear_bit(B_BUS_REQ, &dev->inputs); otg_reset(dev, 0); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_IDLE; spin_unlock_irq(&dev->lock); work = 1; } else if (test_bit(B_SESS_VLD, &dev->inputs)) { pr_debug("b_sess_vld\n"); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_PERIPHERAL; spin_unlock_irq(&dev->lock); msm_otg_start_peripheral(&dev->otg, 1); } else if (test_bit(B_BUS_REQ, &dev->inputs)) { pr_debug("b_sess_end && b_bus_req\n"); ret = msm_otg_start_srp(&dev->otg); if (ret < 0) { /* notify user space */ clear_bit(B_BUS_REQ, &dev->inputs); work = 1; break; } spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_SRP_INIT; spin_unlock_irq(&dev->lock); msm_otg_start_timer(dev, TB_SRP_FAIL, B_SRP_FAIL); break; } else { pr_debug("entering into lpm\n"); msm_otg_suspend(dev); } break; case OTG_STATE_B_SRP_INIT: if (!test_bit(ID, &dev->inputs) || test_bit(B_SESS_VLD, &dev->inputs)) { pr_debug("!id || b_sess_vld\n"); msm_otg_del_timer(dev); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_IDLE; spin_unlock_irq(&dev->lock); work = 1; } else if (test_bit(B_SRP_FAIL, &dev->tmouts)) { pr_debug("b_srp_fail\n"); /* notify user space */ clear_bit(B_BUS_REQ, &dev->inputs); clear_bit(B_SRP_FAIL, &dev->tmouts); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_IDLE; spin_unlock_irq(&dev->lock); dev->b_last_se0_sess = jiffies; work = 1; } break; case OTG_STATE_B_PERIPHERAL: if (!test_bit(ID, &dev->inputs) || !test_bit(B_SESS_VLD, &dev->inputs)) { pr_debug("!id || !b_sess_vld\n"); clear_bit(B_BUS_REQ, &dev->inputs); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_IDLE; spin_unlock_irq(&dev->lock); msm_otg_start_peripheral(&dev->otg, 0); dev->b_last_se0_sess = jiffies; /* Workaround: Reset phy after session */ otg_reset(dev, 1); /* come back later to put hardware in * lpm. This removes addition checks in * suspend routine for missing BSV */ work = 1; } else if (test_bit(B_BUS_REQ, &dev->inputs) && dev->otg.gadget->b_hnp_enable && test_bit(A_BUS_SUSPEND, &dev->inputs)) { pr_debug("b_bus_req && b_hnp_en && a_bus_suspend\n"); msm_otg_start_timer(dev, TB_ASE0_BRST, B_ASE0_BRST); msm_otg_start_peripheral(&dev->otg, 0); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_WAIT_ACON; spin_unlock_irq(&dev->lock); /* start HCD even before A-device enable * pull-up to meet HNP timings. */ dev->otg.host->is_b_host = 1; msm_otg_start_host(&dev->otg, REQUEST_START); } break; case OTG_STATE_B_WAIT_ACON: if (!test_bit(ID, &dev->inputs) || !test_bit(B_SESS_VLD, &dev->inputs)) { pr_debug("!id || !b_sess_vld\n"); msm_otg_del_timer(dev); /* A-device is physically disconnected during * HNP. Remove HCD. */ msm_otg_start_host(&dev->otg, REQUEST_STOP); dev->otg.host->is_b_host = 0; clear_bit(B_BUS_REQ, &dev->inputs); clear_bit(A_BUS_SUSPEND, &dev->inputs); dev->b_last_se0_sess = jiffies; spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_IDLE; spin_unlock_irq(&dev->lock); /* Workaround: Reset phy after session */ otg_reset(dev, 1); work = 1; } else if (test_bit(A_CONN, &dev->inputs)) { pr_debug("a_conn\n"); clear_bit(A_BUS_SUSPEND, &dev->inputs); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_HOST; spin_unlock_irq(&dev->lock); } else if (test_bit(B_ASE0_BRST, &dev->tmouts)) { /* TODO: A-device may send reset after * enabling HNP; a_bus_resume case is * not handled for now. */ pr_debug("b_ase0_brst_tmout\n"); msm_otg_start_host(&dev->otg, REQUEST_STOP); dev->otg.host->is_b_host = 0; clear_bit(B_ASE0_BRST, &dev->tmouts); clear_bit(A_BUS_SUSPEND, &dev->inputs); clear_bit(B_BUS_REQ, &dev->inputs); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_PERIPHERAL; spin_unlock_irq(&dev->lock); msm_otg_start_host(&dev->otg, REQUEST_STOP); } break; case OTG_STATE_B_HOST: /* B_BUS_REQ is not exposed to user space. So * it must be A_CONN for now. */ if (!test_bit(B_BUS_REQ, &dev->inputs) || !test_bit(A_CONN, &dev->inputs)) { pr_debug("!b_bus_req || !a_conn\n"); clear_bit(A_CONN, &dev->inputs); clear_bit(B_BUS_REQ, &dev->inputs); msm_otg_start_host(&dev->otg, 0); dev->otg.host->is_b_host = 0; spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_IDLE; spin_unlock_irq(&dev->lock); /* Workaround: Reset phy after session */ otg_reset(dev, 1); work = 1; } break; case OTG_STATE_A_IDLE: dev->otg.default_a = 1; if (test_bit(ID, &dev->inputs)) { pr_debug("id\n"); dev->otg.default_a = 0; otg_reset(dev, 0); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_IDLE; spin_unlock_irq(&dev->lock); work = 1; } else if (!test_bit(A_BUS_DROP, &dev->inputs) && (test_bit(A_SRP_DET, &dev->inputs) || test_bit(A_BUS_REQ, &dev->inputs))) { pr_debug("!a_bus_drop && (a_srp_det || a_bus_req)\n"); clear_bit(A_SRP_DET, &dev->inputs); /* Disable SRP detection */ writel((readl(USB_OTGSC) & ~OTGSC_INTR_STS_MASK) & ~OTGSC_DPIE, USB_OTGSC); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_VRISE; spin_unlock_irq(&dev->lock); dev->pdata->vbus_power(USB_PHY_INTEGRATED, 1); msm_otg_start_timer(dev, TA_WAIT_VRISE, A_WAIT_VRISE); /* no need to schedule work now */ } else { pr_debug("No session requested\n"); /* A-device is not providing power on VBUS. * Enable SRP detection. */ writel((readl(USB_OTGSC) & ~OTGSC_INTR_STS_MASK) | OTGSC_DPIE, USB_OTGSC); msm_otg_suspend(dev); } break; case OTG_STATE_A_WAIT_VRISE: if (test_bit(ID, &dev->inputs) || test_bit(A_BUS_DROP, &dev->inputs) || test_bit(A_WAIT_VRISE, &dev->tmouts)) { pr_debug("id || a_bus_drop || a_wait_vrise_tmout\n"); clear_bit(A_BUS_REQ, &dev->inputs); msm_otg_del_timer(dev); dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_VFALL; spin_unlock_irq(&dev->lock); msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL); } else if (test_bit(A_VBUS_VLD, &dev->inputs)) { pr_debug("a_vbus_vld\n"); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_BCON; spin_unlock_irq(&dev->lock); msm_otg_start_timer(dev, TA_WAIT_BCON, A_WAIT_BCON); /* Start HCD to detect peripherals. */ msm_otg_start_host(&dev->otg, REQUEST_START); } break; case OTG_STATE_A_WAIT_BCON: if (test_bit(ID, &dev->inputs) || test_bit(A_BUS_DROP, &dev->inputs) || test_bit(A_WAIT_BCON, &dev->tmouts)) { pr_debug("id || a_bus_drop || a_wait_bcon_tmout\n"); msm_otg_del_timer(dev); clear_bit(A_BUS_REQ, &dev->inputs); msm_otg_start_host(&dev->otg, REQUEST_STOP); dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_VFALL; spin_unlock_irq(&dev->lock); msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL); } else if (test_bit(B_CONN, &dev->inputs)) { pr_debug("b_conn\n"); msm_otg_del_timer(dev); /* HCD is added already. just move to * A_HOST state. */ spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_HOST; spin_unlock_irq(&dev->lock); } else if (!test_bit(A_VBUS_VLD, &dev->inputs)) { pr_debug("!a_vbus_vld\n"); msm_otg_del_timer(dev); msm_otg_start_host(&dev->otg, REQUEST_STOP); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_VBUS_ERR; spin_unlock_irq(&dev->lock); } break; case OTG_STATE_A_HOST: if (test_bit(ID, &dev->inputs) || test_bit(A_BUS_DROP, &dev->inputs)) { pr_debug("id || a_bus_drop\n"); clear_bit(B_CONN, &dev->inputs); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_VFALL; spin_unlock_irq(&dev->lock); msm_otg_start_host(&dev->otg, REQUEST_STOP); dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0); msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL); } else if (!test_bit(A_VBUS_VLD, &dev->inputs)) { pr_debug("!a_vbus_vld\n"); clear_bit(B_CONN, &dev->inputs); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_VBUS_ERR; spin_unlock_irq(&dev->lock); msm_otg_start_host(&dev->otg, REQUEST_STOP); /* no work */ } else if (!test_bit(A_BUS_REQ, &dev->inputs)) { /* a_bus_req is de-asserted when root hub is * suspended or HNP is in progress. */ pr_debug("!a_bus_req\n"); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_SUSPEND; spin_unlock_irq(&dev->lock); if (dev->otg.host->b_hnp_enable) { msm_otg_start_timer(dev, TA_AIDL_BDIS, A_AIDL_BDIS); } else { /* No HNP. Root hub suspended */ msm_otg_suspend(dev); } } else if (!test_bit(B_CONN, &dev->inputs)) { pr_debug("!b_conn\n"); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_BCON; spin_unlock_irq(&dev->lock); msm_otg_start_timer(dev, TA_WAIT_BCON, A_WAIT_BCON); } break; case OTG_STATE_A_SUSPEND: if (test_bit(ID, &dev->inputs) || test_bit(A_BUS_DROP, &dev->inputs) || test_bit(A_AIDL_BDIS, &dev->tmouts)) { pr_debug("id || a_bus_drop || a_aidl_bdis_tmout\n"); msm_otg_del_timer(dev); clear_bit(B_CONN, &dev->inputs); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_VFALL; spin_unlock_irq(&dev->lock); msm_otg_start_host(&dev->otg, REQUEST_STOP); dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0); msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL); } else if (!test_bit(A_VBUS_VLD, &dev->inputs)) { pr_debug("!a_vbus_vld\n"); msm_otg_del_timer(dev); clear_bit(B_CONN, &dev->inputs); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_VBUS_ERR; spin_unlock_irq(&dev->lock); msm_otg_start_host(&dev->otg, REQUEST_STOP); } else if (!test_bit(B_CONN, &dev->inputs) && dev->otg.host->b_hnp_enable) { pr_debug("!b_conn && b_hnp_enable"); /* Clear AIDL_BDIS timer */ msm_otg_del_timer(dev); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_PERIPHERAL; spin_unlock_irq(&dev->lock); msm_otg_start_host(&dev->otg, REQUEST_HNP_SUSPEND); /* We may come here even when B-dev is physically * disconnected during HNP. We go back to host * role if bus is idle for BIDL_ADIS time. */ dev->otg.gadget->is_a_peripheral = 1; msm_otg_start_peripheral(&dev->otg, 1); } else if (!test_bit(B_CONN, &dev->inputs) && !dev->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, &dev->inputs); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_BCON; spin_unlock_irq(&dev->lock); msm_otg_start_timer(dev, TA_WAIT_BCON, A_WAIT_BCON); } break; case OTG_STATE_A_PERIPHERAL: if (test_bit(ID, &dev->inputs) || test_bit(A_BUS_DROP, &dev->inputs)) { pr_debug("id || a_bus_drop\n"); /* Clear BIDL_ADIS timer */ msm_otg_del_timer(dev); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_VFALL; spin_unlock_irq(&dev->lock); msm_otg_start_peripheral(&dev->otg, 0); dev->otg.gadget->is_a_peripheral = 0; /* HCD was suspended before. Stop it now */ msm_otg_start_host(&dev->otg, REQUEST_STOP); dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0); msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL); } else if (!test_bit(A_VBUS_VLD, &dev->inputs)) { pr_debug("!a_vbus_vld\n"); /* Clear BIDL_ADIS timer */ msm_otg_del_timer(dev); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_VBUS_ERR; spin_unlock_irq(&dev->lock); msm_otg_start_peripheral(&dev->otg, 0); dev->otg.gadget->is_a_peripheral = 0; /* HCD was suspended before. Stop it now */ msm_otg_start_host(&dev->otg, REQUEST_STOP); } else if (test_bit(A_BIDL_ADIS, &dev->tmouts)) { pr_debug("a_bidl_adis_tmout\n"); msm_otg_start_peripheral(&dev->otg, 0); dev->otg.gadget->is_a_peripheral = 0; spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_BCON; spin_unlock_irq(&dev->lock); set_bit(A_BUS_REQ, &dev->inputs); msm_otg_start_host(&dev->otg, REQUEST_HNP_RESUME); msm_otg_start_timer(dev, TA_WAIT_BCON, A_WAIT_BCON); } break; case OTG_STATE_A_WAIT_VFALL: if (test_bit(A_WAIT_VFALL, &dev->tmouts)) { clear_bit(A_VBUS_VLD, &dev->inputs); /* Reset both phy and link */ otg_reset(dev, 1); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_IDLE; spin_unlock_irq(&dev->lock); work = 1; } break; case OTG_STATE_A_VBUS_ERR: if (test_bit(ID, &dev->inputs) || test_bit(A_BUS_DROP, &dev->inputs) || test_bit(A_CLR_ERR, &dev->inputs)) { spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_VFALL; spin_unlock_irq(&dev->lock); dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0); msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL); } break; default: pr_err("invalid OTG state\n"); } if (work) queue_work(dev->wq, &dev->sm_work); /* IRQ/sysfs may queue work. Check work_pending. otherwise * we might endup releasing wakelock after it is acquired * in IRQ/sysfs. */ if (!work_pending(&dev->sm_work) && !hrtimer_active(&dev->timer)) wake_unlock(&dev->wlock); }
static int __init msm_otg_probe(struct platform_device *pdev) { int ret = 0; struct resource *res; struct msm_otg *dev; struct msm_otg_platform_data *pdata; dev = kzalloc(sizeof(struct msm_otg), GFP_KERNEL); if (!dev) return -ENOMEM; dev->otg.dev = &pdev->dev; if (pdev->dev.platform_data) { pdata = pdev->dev.platform_data; dev->rpc_connect = pdata->rpc_connect; dev->phy_reset = pdata->phy_reset; } if (dev->rpc_connect) { ret = dev->rpc_connect(1); pr_info("%s: rpc_connect(%d)\n", __func__, ret); if (ret) { pr_err("%s: rpc connect failed\n", __func__); ret = -ENODEV; goto free_dev; } } dev->clk = clk_get(&pdev->dev, "usb_hs_clk"); if (IS_ERR(dev->clk)) { pr_err("%s: failed to get usb_hs_clk\n", __func__); ret = PTR_ERR(dev->clk); goto rpc_fail; } dev->pclk = clk_get(&pdev->dev, "usb_hs_pclk"); if (IS_ERR(dev->clk)) { pr_err("%s: failed to get usb_hs_pclk\n", __func__); ret = PTR_ERR(dev->pclk); goto put_clk; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { pr_err("%s: failed to get platform resource mem\n", __func__); ret = -ENODEV; goto put_pclk; } dev->regs = ioremap(res->start, resource_size(res)); if (!dev->regs) { pr_err("%s: ioremap failed\n", __func__); ret = -ENOMEM; goto put_pclk; } dev->irq = platform_get_irq(pdev, 0); if (!dev->irq) { pr_err("%s: platform_get_irq failed\n", __func__); ret = -ENODEV; goto free_regs; } /* enable clocks */ clk_enable(dev->clk); clk_enable(dev->pclk); otg_reset(dev); ret = request_irq(dev->irq, msm_otg_irq, IRQF_SHARED, "msm_otg", dev); if (ret) { pr_info("%s: request irq failed\n", __func__); clk_disable(dev->clk); clk_disable(dev->pclk); goto free_regs; } the_msm_otg = dev; dev->otg.set_peripheral = msm_otg_set_peripheral; dev->otg.set_host = msm_otg_set_host; dev->otg.set_suspend = msm_otg_set_suspend; if (otg_set_transceiver(&dev->otg)) { WARN_ON(1); goto free_regs; } wake_lock_init(&dev->wlock, WAKE_LOCK_SUSPEND, "usb_bus_active"); wake_lock(&dev->wlock); msm_otg_debugfs_init(dev); device_init_wakeup(&pdev->dev, 1); return 0; free_regs: iounmap(dev->regs); put_pclk: clk_put(dev->pclk); put_clk: clk_put(dev->clk); rpc_fail: dev->rpc_connect(0); free_dev: kfree(dev); return ret; }
static int __init msm_otg_probe(struct platform_device *pdev) { int ret = 0; int vbus_on_irq = 0; struct resource *res; struct msm_otg *dev; struct msm_otg_platform_data *pdata; dev = kzalloc(sizeof(struct msm_otg), GFP_KERNEL); if (!dev) return -ENOMEM; dev->otg.dev = &pdev->dev; pdata = pdev->dev.platform_data; if (pdev->dev.platform_data) { dev->rpc_connect = pdata->rpc_connect; dev->phy_reset = pdata->phy_reset; dev->core_clk = pdata->core_clk; /* pmic apis */ dev->pmic_notif_init = pdata->pmic_notif_init; dev->pmic_notif_deinit = pdata->pmic_notif_deinit; dev->pmic_register_vbus_sn = pdata->pmic_register_vbus_sn; dev->pmic_unregister_vbus_sn = pdata->pmic_unregister_vbus_sn; dev->pmic_enable_ldo = pdata->pmic_enable_ldo; } if (pdata && pdata->pmic_vbus_irq) { vbus_on_irq = platform_get_irq_byname(pdev, "vbus_on"); if (vbus_on_irq < 0) { pr_err("%s: unable to get vbus on irq\n", __func__); ret = vbus_on_irq; goto free_dev; } } if (dev->rpc_connect) { ret = dev->rpc_connect(1); pr_info("%s: rpc_connect(%d)\n", __func__, ret); if (ret) { pr_err("%s: rpc connect failed\n", __func__); ret = -ENODEV; goto free_dev; } } dev->clk = clk_get(&pdev->dev, "usb_hs_clk"); if (IS_ERR(dev->clk)) { pr_err("%s: failed to get usb_hs_clk\n", __func__); ret = PTR_ERR(dev->clk); goto rpc_fail; } dev->pclk = clk_get(&pdev->dev, "usb_hs_pclk"); if (IS_ERR(dev->pclk)) { pr_err("%s: failed to get usb_hs_pclk\n", __func__); ret = PTR_ERR(dev->pclk); goto put_clk; } if (dev->core_clk) { dev->cclk = clk_get(&pdev->dev, "usb_hs_core_clk"); if (IS_ERR(dev->cclk)) { pr_err("%s: failed to get usb_hs_core_clk\n", __func__); ret = PTR_ERR(dev->cclk); goto put_pclk; } } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { pr_err("%s: failed to get platform resource mem\n", __func__); ret = -ENODEV; goto put_cclk; } dev->regs = ioremap(res->start, resource_size(res)); if (!dev->regs) { pr_err("%s: ioremap failed\n", __func__); ret = -ENOMEM; goto put_cclk; } dev->irq = platform_get_irq(pdev, 0); if (!dev->irq) { pr_err("%s: platform_get_irq failed\n", __func__); ret = -ENODEV; goto free_regs; } /* enable clocks */ clk_enable(dev->pclk); if (dev->cclk) clk_enable(dev->cclk); /* To reduce phy power consumption and to avoid external LDO * on the board, PMIC comparators can be used to detect VBUS * session change. */ if (dev->pmic_notif_init) { ret = dev->pmic_notif_init(); if (!ret) { dev->pmic_notif_supp = 1; dev->pmic_enable_ldo(1); } else if (ret != -ENOTSUPP) { clk_disable(dev->pclk); if (dev->cclk) clk_disable(dev->cclk); goto free_regs; } } otg_reset(dev); ret = request_irq(dev->irq, msm_otg_irq, IRQF_SHARED, "msm_otg", dev); if (ret) { pr_info("%s: request irq failed\n", __func__); clk_disable(dev->pclk); if (dev->cclk) clk_disable(dev->cclk); goto free_regs; } the_msm_otg = dev; dev->vbus_on_irq = vbus_on_irq; dev->otg.set_peripheral = msm_otg_set_peripheral; dev->otg.set_host = msm_otg_set_host; dev->otg.set_suspend = msm_otg_set_suspend; dev->set_clk = msm_otg_set_clk; if (otg_set_transceiver(&dev->otg)) { WARN_ON(1); goto free_otg_irq; } device_init_wakeup(&pdev->dev, 1); if (vbus_on_irq) { ret = request_irq(vbus_on_irq, pmic_vbus_on_irq, IRQF_TRIGGER_RISING, "msm_otg_vbus_on", NULL); if (ret) { pr_info("%s: request_irq for vbus_on" "interrupt failed\n", __func__); goto free_otg_irq; } } return 0; free_otg_irq: free_irq(dev->irq, dev); free_regs: iounmap(dev->regs); put_cclk: if (dev->cclk) clk_put(dev->cclk); put_pclk: clk_put(dev->pclk); put_clk: clk_put(dev->clk); rpc_fail: dev->rpc_connect(0); free_dev: kfree(dev); return ret; }
static int msm_otg_suspend(struct msm_otg *dev) { unsigned long timeout; int vbus = 0; unsigned otgsc; disable_irq(dev->irq); if (dev->in_lpm) goto out; /* Don't reset if mini-A cable is connected */ if (!is_host()) otg_reset(dev); /* In case of fast plug-in and plug-out inside the otg_reset() the * servicing of BSV is missed (in the window of after phy and link * reset). Handle it if any missing bsv is detected */ if (is_b_sess_vld() && !is_host()) { otgsc = readl(USB_OTGSC); writel(otgsc, USB_OTGSC); pr_info("%s:Process mising BSV\n", __func__); msm_otg_start_peripheral(&dev->otg, 1); enable_irq(dev->irq); return -1; } ulpi_read(dev, 0x14);/* clear PHY interrupt latch register */ /* If there is no pmic notify support turn on phy comparators. */ if (!dev->pmic_notif_supp) ulpi_write(dev, 0x01, 0x30); ulpi_write(dev, 0x08, 0x09);/* turn off PLL on integrated phy */ timeout = jiffies + msecs_to_jiffies(500); disable_phy_clk(); while (!is_phy_clk_disabled()) { if (time_after(jiffies, timeout)) { pr_err("%s: Unable to suspend phy\n", __func__); otg_reset(dev); goto out; } msleep(1); } writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); clk_disable(dev->pclk); if (dev->cclk) clk_disable(dev->cclk); if (device_may_wakeup(dev->otg.dev)) { enable_irq_wake(dev->irq); if (dev->vbus_on_irq) enable_irq_wake(dev->vbus_on_irq); } dev->in_lpm = 1; if (!vbus && dev->pmic_notif_supp) dev->pmic_enable_ldo(0); pr_info("%s: usb in low power mode\n", __func__); out: enable_irq(dev->irq); /* TBD: as there is no bus suspend implemented as of now * it should be dummy check */ return 0; }