static irqreturn_t hsic_peripheral_status_change(int irq, void *dev_id) { struct msm_hsic_hcd *mehci = dev_id; pr_debug("%s: mehci:%p dev_id:%p\n", __func__, mehci, dev_id); if (mehci) msm_hsic_config_gpios(mehci, 0); return IRQ_HANDLED; }
static int __devexit ehci_hsic_msm_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd); struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data; if (pdata && pdata->swfi_latency) pm_qos_remove_request(&mehci->pm_qos_req_dma); if (mehci->peripheral_status_irq) free_irq(mehci->peripheral_status_irq, mehci); if (mehci->wakeup_irq) { if (mehci->wakeup_irq_enabled) disable_irq_wake(mehci->wakeup_irq); free_irq(mehci->wakeup_irq, mehci); } /* * If the update request is called after unregister, the request will * fail. Results are undefined if unregister is called in the middle of * update request. */ mehci->bus_vote = false; cancel_work_sync(&mehci->bus_vote_w); if (mehci->bus_perf_client) msm_bus_scale_unregister_client(mehci->bus_perf_client); ehci_hsic_msm_debugfs_cleanup(); device_init_wakeup(&pdev->dev, 0); pm_runtime_set_suspended(&pdev->dev); destroy_workqueue(ehci_wq); usb_remove_hcd(hcd); msm_hsic_config_gpios(mehci, 0); msm_hsic_init_vddcx(mehci, 0); msm_hsic_init_clocks(mehci, 0); wake_lock_destroy(&mehci->wlock); iounmap(hcd->regs); usb_put_hcd(hcd); return 0; }
static int __devexit ehci_hsic_msm_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd); struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data; if (pdata && pdata->swfi_latency) pm_qos_remove_request(&mehci->pm_qos_req_dma); if (mehci->peripheral_status_irq) free_irq(mehci->peripheral_status_irq, mehci); if (mehci->wakeup_irq) { if (mehci->wakeup_irq_enabled) disable_irq_wake(mehci->wakeup_irq); free_irq(mehci->wakeup_irq, mehci); } if (mehci->bus_perf_client) msm_bus_scale_unregister_client(mehci->bus_perf_client); ehci_hsic_msm_debugfs_cleanup(); device_init_wakeup(&pdev->dev, 0); pm_runtime_set_suspended(&pdev->dev); usb_remove_hcd(hcd); msm_hsic_config_gpios(mehci, 0); msm_hsic_init_vddcx(mehci, 0); msm_hsic_init_clocks(mehci, 0); wake_lock_destroy(&mehci->wlock); iounmap(hcd->regs); usb_put_hcd(hcd); return 0; }
static int msm_hsic_resume(struct msm_hsic_hcd *mehci) { struct usb_hcd *hcd = hsic_to_hcd(mehci); int cnt = 0, ret; unsigned temp; int min_vol, max_vol; if (!atomic_read(&mehci->in_lpm)) { dev_dbg(mehci->dev, "%s called in !in_lpm\n", __func__); return 0; } if (mehci->wakeup_irq_enabled) { disable_irq_wake(mehci->wakeup_irq); disable_irq_nosync(mehci->wakeup_irq); mehci->wakeup_irq_enabled = 0; } wake_lock(&mehci->wlock); if (mehci->bus_perf_client && debug_bus_voting_enabled) { mehci->bus_vote = true; queue_work(ehci_wq, &mehci->bus_vote_w); } min_vol = vdd_val[mehci->vdd_type][VDD_MIN]; max_vol = vdd_val[mehci->vdd_type][VDD_MAX]; ret = regulator_set_voltage(mehci->hsic_vddcx, min_vol, max_vol); if (ret < 0) dev_err(mehci->dev, "unable to set nominal vddcx voltage (no VDD MIN)\n"); clk_prepare_enable(mehci->core_clk); clk_prepare_enable(mehci->phy_clk); clk_prepare_enable(mehci->cal_clk); clk_prepare_enable(mehci->ahb_clk); temp = readl_relaxed(USB_USBCMD); temp &= ~ASYNC_INTR_CTRL; temp &= ~ULPI_STP_CTRL; writel_relaxed(temp, USB_USBCMD); if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD)) goto skip_phy_resume; temp = readl_relaxed(USB_PORTSC); temp &= ~(PORT_RWC_BITS | PORTSC_PHCD); writel_relaxed(temp, USB_PORTSC); while (cnt < PHY_RESUME_TIMEOUT_USEC) { if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD) && (readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_SYNC_STATE)) break; udelay(1); cnt++; } if (cnt >= PHY_RESUME_TIMEOUT_USEC) { /* * This is a fatal error. Reset the link and * PHY to make hsic working. */ dev_err(mehci->dev, "Unable to resume USB. Reset the hsic\n"); msm_hsic_config_gpios(mehci, 0); msm_hsic_reset(mehci); } skip_phy_resume: usb_hcd_resume_root_hub(hcd); atomic_set(&mehci->in_lpm, 0); if (mehci->async_int) { mehci->async_int = false; pm_runtime_put_noidle(mehci->dev); enable_irq(hcd->irq); } if (atomic_read(&mehci->pm_usage_cnt)) { atomic_set(&mehci->pm_usage_cnt, 0); pm_runtime_put_noidle(mehci->dev); } dev_dbg(mehci->dev, "HSIC-USB exited from low power mode\n"); return 0; }
static int msm_hsic_suspend(struct msm_hsic_hcd *mehci) { struct usb_hcd *hcd = hsic_to_hcd(mehci); int cnt = 0, ret; u32 val; int none_vol, max_vol; if (atomic_read(&mehci->in_lpm)) { dev_dbg(mehci->dev, "%s called in lpm\n", __func__); return 0; } if (!(readl_relaxed(USB_PORTSC) & PORT_PE)) { dev_dbg(mehci->dev, "%s:port is not enabled skip suspend\n", __func__); return -EAGAIN; } disable_irq(hcd->irq); /* make sure we don't race against a remote wakeup */ if (test_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags) || readl_relaxed(USB_PORTSC) & PORT_RESUME) { dev_dbg(mehci->dev, "wakeup pending, aborting suspend\n"); enable_irq(hcd->irq); return -EBUSY; } /* * PHY may take some time or even fail to enter into low power * mode (LPM). Hence poll for 500 msec and reset the PHY and link * in failure case. */ val = readl_relaxed(USB_PORTSC); val &= ~PORT_RWC_BITS; val |= PORTSC_PHCD; writel_relaxed(val, USB_PORTSC); while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { if (readl_relaxed(USB_PORTSC) & PORTSC_PHCD) break; udelay(1); cnt++; } if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) { dev_err(mehci->dev, "Unable to suspend PHY\n"); msm_hsic_config_gpios(mehci, 0); msm_hsic_reset(mehci); } /* * PHY has capability to generate interrupt asynchronously in low * power mode (LPM). This interrupt is level triggered. So USB IRQ * line must be disabled till async interrupt enable bit is cleared * in USBCMD register. Assert STP (ULPI interface STOP signal) to * block data communication from PHY. */ writel_relaxed(readl_relaxed(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); /* * Ensure that hardware is put in low power mode before * clocks are turned OFF and VDD is allowed to minimize. */ mb(); clk_disable_unprepare(mehci->core_clk); clk_disable_unprepare(mehci->phy_clk); clk_disable_unprepare(mehci->cal_clk); clk_disable_unprepare(mehci->ahb_clk); none_vol = vdd_val[mehci->vdd_type][VDD_NONE]; max_vol = vdd_val[mehci->vdd_type][VDD_MAX]; ret = regulator_set_voltage(mehci->hsic_vddcx, none_vol, max_vol); if (ret < 0) dev_err(mehci->dev, "unable to set vddcx voltage for VDD MIN\n"); if (mehci->bus_perf_client && debug_bus_voting_enabled) { mehci->bus_vote = false; queue_work(ehci_wq, &mehci->bus_vote_w); } atomic_set(&mehci->in_lpm, 1); enable_irq(hcd->irq); mehci->wakeup_irq_enabled = 1; enable_irq_wake(mehci->wakeup_irq); enable_irq(mehci->wakeup_irq); wake_unlock(&mehci->wlock); dev_dbg(mehci->dev, "HSIC-USB in low power mode\n"); return 0; }
static int msm_hsic_reset(struct msm_hsic_hcd *mehci) { struct usb_hcd *hcd = hsic_to_hcd(mehci); int ret; struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data; msm_hsic_clk_reset(mehci); /* select ulpi phy */ writel_relaxed(0x80000000, USB_PORTSC); mb(); /* HSIC init sequence when HSIC signals (Strobe/Data) are routed via GPIOs */ if (pdata && pdata->strobe && pdata->data) { /* Enable LV_MODE in HSIC_CAL_PAD_CTL register */ writel_relaxed(HSIC_LV_MODE, HSIC_CAL_PAD_CTL); mb(); /*set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */ ulpi_write(mehci, 0xFF, 0x33); /* Enable periodic IO calibration in HSIC_CFG register */ ulpi_write(mehci, HSIC_PAD_CALIBRATION, 0x30); /* Configure GPIO pins for HSIC functionality mode */ ret = msm_hsic_config_gpios(mehci, 1); if (ret) { dev_err(mehci->dev, " gpio configuarion failed\n"); return ret; } /* Set LV_MODE=0x1 and DCC=0x2 in HSIC_GPIO PAD_CTL register */ writel_relaxed(HSIC_GPIO_PAD_VAL, HSIC_STROBE_GPIO_PAD_CTL); writel_relaxed(HSIC_GPIO_PAD_VAL, HSIC_DATA_GPIO_PAD_CTL); mb(); /* Enable HSIC mode in HSIC_CFG register */ ulpi_write(mehci, 0x01, 0x31); } else { /* HSIC init sequence when HSIC signals (Strobe/Data) are routed via dedicated I/O */ /* programmable length of connect signaling (33.2ns) */ ret = ulpi_write(mehci, 3, HSIC_DBG1_REG); if (ret) { pr_err("%s: Unable to program length of connect " "signaling\n", __func__); } /*set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */ ulpi_write(mehci, 0xFF, 0x33); /* Enable HSIC mode in HSIC_CFG register */ ulpi_write(mehci, 0xA9, 0x30); } /*disable auto resume*/ ulpi_write(mehci, ULPI_IFC_CTRL_AUTORESUME, ULPI_CLR(ULPI_IFC_CTRL)); return 0; }
static int __devinit ehci_hsic_msm_probe(struct platform_device *pdev) { struct usb_hcd *hcd; struct resource *res; struct msm_hsic_hcd *mehci; struct msm_hsic_host_platform_data *pdata; int ret; dev_dbg(&pdev->dev, "ehci_msm-hsic probe\n"); /* After parent device's probe is executed, it will be put in suspend * mode. When child device's probe is called, driver core is not * resuming parent device due to which parent will be in suspend even * though child is active. Hence resume the parent device explicitly. */ if (pdev->dev.parent) pm_runtime_get_sync(pdev->dev.parent); hcd = usb_create_hcd(&msm_hsic_driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { dev_err(&pdev->dev, "Unable to create HCD\n"); return -ENOMEM; } hcd_to_bus(hcd)->skip_resume = true; hcd->irq = platform_get_irq(pdev, 0); if (hcd->irq < 0) { dev_err(&pdev->dev, "Unable to get IRQ resource\n"); ret = hcd->irq; goto put_hcd; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "Unable to get memory resource\n"); ret = -ENODEV; goto put_hcd; } hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); if (!hcd->regs) { dev_err(&pdev->dev, "ioremap failed\n"); ret = -ENOMEM; goto put_hcd; } mehci = hcd_to_hsic(hcd); mehci->dev = &pdev->dev; pdata = mehci->dev->platform_data; mehci->ehci.susp_sof_bug = 1; mehci->ehci.reset_sof_bug = 1; mehci->ehci.resume_sof_bug = 1; if (pdata) mehci->ehci.log2_irq_thresh = pdata->log2_irq_thresh; res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "peripheral_status_irq"); if (res) mehci->peripheral_status_irq = res->start; res = platform_get_resource_byname(pdev, IORESOURCE_IO, "wakeup"); if (res) { mehci->wakeup_gpio = res->start; mehci->wakeup_irq = MSM_GPIO_TO_INT(res->start); dev_dbg(mehci->dev, "wakeup_irq: %d\n", mehci->wakeup_irq); } ret = msm_hsic_init_clocks(mehci, 1); if (ret) { dev_err(&pdev->dev, "unable to initialize clocks\n"); ret = -ENODEV; goto unmap; } ret = msm_hsic_init_vddcx(mehci, 1); if (ret) { dev_err(&pdev->dev, "unable to initialize VDDCX\n"); ret = -ENODEV; goto deinit_clocks; } init_completion(&mehci->rt_completion); init_completion(&mehci->gpt0_completion); ret = msm_hsic_reset(mehci); if (ret) { dev_err(&pdev->dev, "unable to initialize PHY\n"); goto deinit_vddcx; } ehci_wq = create_singlethread_workqueue("ehci_wq"); if (!ehci_wq) { dev_err(&pdev->dev, "unable to create workqueue\n"); ret = -ENOMEM; goto deinit_vddcx; } INIT_WORK(&mehci->bus_vote_w, ehci_hsic_bus_vote_w); ret = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); if (ret) { dev_err(&pdev->dev, "unable to register HCD\n"); goto unconfig_gpio; } device_init_wakeup(&pdev->dev, 1); wake_lock_init(&mehci->wlock, WAKE_LOCK_SUSPEND, dev_name(&pdev->dev)); wake_lock(&mehci->wlock); if (mehci->peripheral_status_irq) { ret = request_threaded_irq(mehci->peripheral_status_irq, NULL, hsic_peripheral_status_change, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED, "hsic_peripheral_status", mehci); if (ret) dev_err(&pdev->dev, "%s:request_irq:%d failed:%d", __func__, mehci->peripheral_status_irq, ret); } /* configure wakeup irq */ if (mehci->wakeup_irq) { ret = request_irq(mehci->wakeup_irq, msm_hsic_wakeup_irq, IRQF_TRIGGER_HIGH, "msm_hsic_wakeup", mehci); if (!ret) { disable_irq_nosync(mehci->wakeup_irq); } else { dev_err(&pdev->dev, "request_irq(%d) failed: %d\n", mehci->wakeup_irq, ret); mehci->wakeup_irq = 0; } } ret = ehci_hsic_msm_debugfs_init(mehci); if (ret) dev_dbg(&pdev->dev, "mode debugfs file is" "not available\n"); if (pdata && pdata->bus_scale_table) { mehci->bus_perf_client = msm_bus_scale_register_client(pdata->bus_scale_table); /* Configure BUS performance parameters for MAX bandwidth */ if (mehci->bus_perf_client) { mehci->bus_vote = true; queue_work(ehci_wq, &mehci->bus_vote_w); } else { dev_err(&pdev->dev, "%s: Failed to register BUS " "scaling client!!\n", __func__); } } __mehci = mehci; /* * This pdev->dev is assigned parent of root-hub by USB core, * hence, runtime framework automatically calls this driver's * runtime APIs based on root-hub's state. */ pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); /* Decrement the parent device's counter after probe. * As child is active, parent will not be put into * suspend mode. */ if (pdev->dev.parent) pm_runtime_put_sync(pdev->dev.parent); return 0; unconfig_gpio: destroy_workqueue(ehci_wq); msm_hsic_config_gpios(mehci, 0); deinit_vddcx: msm_hsic_init_vddcx(mehci, 0); deinit_clocks: msm_hsic_init_clocks(mehci, 0); unmap: iounmap(hcd->regs); put_hcd: usb_put_hcd(hcd); return ret; }