aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ehci-s5p.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/ehci-s5p.c')
-rw-r--r--drivers/usb/host/ehci-s5p.c165
1 files changed, 156 insertions, 9 deletions
diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
index 8cb7ae2..78399c2 100644
--- a/drivers/usb/host/ehci-s5p.c
+++ b/drivers/usb/host/ehci-s5p.c
@@ -24,6 +24,18 @@
#include <mach/regs-usb-host.h>
#include <mach/board_rev.h>
+#ifdef CONFIG_MDM_HSIC_PM
+#include <linux/mdm_hsic_pm.h>
+static const char hsic_pm_dev[] = "mdm_hsic_pm0";
+#endif
+
+#if defined(CONFIG_EHCI_IRQ_DISTRIBUTION)
+#include <linux/cpu.h>
+#endif
+#if defined(CONFIG_LINK_DEVICE_HSIC) || defined(CONFIG_LINK_DEVICE_USB)
+#include <mach/sec_modem.h>
+#endif
+
struct s5p_ehci_hcd {
struct device *dev;
struct usb_hcd *hcd;
@@ -81,14 +93,22 @@ static int s5p_ehci_configurate(struct usb_hcd *hcd)
delay_count);
/* DMA burst Enable, set utmi suspend_on_n */
- writel(readl(INSNREG00(hcd->regs)) | ENA_DMA_INCR | OHCI_SUSP_LGCY,
+#ifdef CONFIG_USB_OHCI_S5P
+ writel(readl(INSNREG00(hcd->regs)) | ENA_DMA_INCR,
+#else
+ writel(readl(INSNREG00(hcd->regs)) | ENA_DMA_INCR,
+#endif
INSNREG00(hcd->regs));
return 0;
}
#if defined(CONFIG_LINK_DEVICE_HSIC) || defined(CONFIG_LINK_DEVICE_USB) ||\
- defined(CONFIG_CDMA_MODEM_MDM6600)
-#define CP_PORT 2 /* HSIC0 in S5PC210 */
+ defined(CONFIG_CDMA_MODEM_MDM6600) || defined(CONFIG_MDM_HSIC_PM)
+#ifdef CONFIG_MACH_P8LTE
+#define CP_PORT 1 /* HSIC0 in S5PC210 */
+#else
+#define CP_PORT 2 /* HSIC0 in S5PC210 */
+#endif
#define RETRY_CNT_LIMIT 30 /* Max 300ms wait for cp resume*/
int s5p_ehci_port_control(struct platform_device *pdev, int port, int enable)
@@ -105,7 +125,10 @@ int s5p_ehci_port_control(struct platform_device *pdev, int port, int enable)
ehci_readl(ehci, &ehci->regs->command);
return 0;
}
+#endif
+#if defined(CONFIG_LINK_DEVICE_HSIC) || defined(CONFIG_LINK_DEVICE_USB) \
+ || defined(CONFIG_MDM_HSIC_PM)
static void s5p_wait_for_cp_resume(struct platform_device *pdev,
struct usb_hcd *hcd)
{
@@ -114,11 +137,17 @@ static void s5p_wait_for_cp_resume(struct platform_device *pdev,
u32 __iomem *portsc ;
u32 val32, retry_cnt = 0;
+#if !defined(CONFIG_MDM_HSIC_PM)
+ /* when use usb3503 hub, need not wait cp resume */
+ if (modem_using_hub())
+ return;
+#endif
portsc = &ehci->regs->port_status[CP_PORT-1];
+#if !defined(CONFIG_MDM_HSIC_PM)
if (pdata && pdata->noti_host_states)
pdata->noti_host_states(pdev, S5P_HOST_ON);
-
+#endif
do {
msleep(10);
val32 = ehci_readl(ehci, portsc);
@@ -172,6 +201,29 @@ static int s5p_ehci_suspend(struct device *dev)
unsigned long flags;
int rc = 0;
+#ifdef CONFIG_MDM_HSIC_PM
+ /*
+ * check suspend returns 1 if it is possible to suspend
+ * otherwise, it returns 0 impossible or returns some error
+ */
+ rc = check_udev_suspend_allowed(hsic_pm_dev);
+ if (rc > 0) {
+ set_host_stat(hsic_pm_dev, POWER_OFF);
+ if (wait_dev_pwr_stat(hsic_pm_dev, POWER_OFF) < 0) {
+ set_host_stat(hsic_pm_dev, POWER_ON);
+ pm_runtime_resume(&pdev->dev);
+ return -EBUSY;
+ }
+ } else if (rc == -ENODEV) {
+ /* no hsic pm driver loaded, proceed suspend */
+ pr_debug("%s: suspend without hsic pm\n", __func__);
+ } else {
+ pm_runtime_resume(&pdev->dev);
+ return -EBUSY;
+ }
+ rc = 0;
+#endif
+
if (time_before(jiffies, ehci->next_statechange))
msleep(10);
@@ -261,7 +313,12 @@ static int s5p_ehci_resume(struct device *dev)
ehci_port_power(ehci, 1);
hcd->state = HC_STATE_SUSPENDED;
-#if defined(CONFIG_LINK_DEVICE_HSIC) || defined(CONFIG_LINK_DEVICE_USB)
+#ifdef CONFIG_MDM_HSIC_PM
+ set_host_stat(hsic_pm_dev, POWER_ON);
+ wait_dev_pwr_stat(hsic_pm_dev, POWER_ON);
+#endif
+#if defined(CONFIG_LINK_DEVICE_HSIC) || defined(CONFIG_LINK_DEVICE_USB) \
+ || defined(CONFIG_MDM_HSIC_PM)
s5p_wait_for_cp_resume(pdev, hcd);
#endif
return 0;
@@ -280,7 +337,9 @@ static int s5p_ehci_runtime_suspend(struct device *dev)
if (pdata && pdata->phy_suspend)
pdata->phy_suspend(pdev, S5P_USB_PHY_HOST);
-
+#ifdef CONFIG_MDM_HSIC_PM
+ request_active_lock_release(hsic_pm_dev);
+#endif
return 0;
}
@@ -296,6 +355,9 @@ static int s5p_ehci_runtime_resume(struct device *dev)
if (dev->power.is_suspended)
return 0;
+#ifdef CONFIG_MDM_HSIC_PM
+ request_active_lock_set(hsic_pm_dev);
+#endif
/* platform device isn't suspended */
if (pdata && pdata->phy_resume)
rc = pdata->phy_resume(pdev, S5P_USB_PHY_HOST);
@@ -313,14 +375,17 @@ static int s5p_ehci_runtime_resume(struct device *dev)
usb_root_hub_lost_power(hcd->self.root_hub);
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
- ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
- (void)ehci_readl(ehci, &ehci->regs->intr_enable);
/* here we "know" root ports should always stay powered */
ehci_port_power(ehci, 1);
hcd->state = HC_STATE_SUSPENDED;
-#if defined(CONFIG_LINK_DEVICE_HSIC) || defined(CONFIG_LINK_DEVICE_USB)
+#ifdef CONFIG_MDM_HSIC_PM
+ set_host_stat(hsic_pm_dev, POWER_ON);
+ wait_dev_pwr_stat(hsic_pm_dev, POWER_ON);
+#endif
+#if defined(CONFIG_LINK_DEVICE_HSIC) || defined(CONFIG_LINK_DEVICE_USB) \
+ || defined(CONFIG_MDM_HSIC_PM)
s5p_wait_for_cp_resume(pdev, hcd);
#endif
}
@@ -497,6 +562,50 @@ static inline void remove_ehci_sys_file(struct ehci_hcd *ehci)
#endif
}
+#if defined(CONFIG_EHCI_IRQ_DISTRIBUTION)
+static int s5p_ehci_irq_no = 0;
+static int s5p_ehci_irq_cpu = 0;
+
+/* total cpu core numbers to irq cpu (cpu0 is default)
+ * 1 (single): cpu0
+ * 2 (dual) : cpu1
+ * 3 : cpu1
+ * 4 (quad) : cpu3
+ */
+static int s5p_ehci_cpus[] = {0, 1, 1, 3};
+
+static int __cpuinit s5p_ehci_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ int cpu = (unsigned long)hcpu;
+
+ if (!s5p_ehci_irq_no || cpu != s5p_ehci_irq_cpu)
+ goto exit;
+
+ switch (action) {
+ case CPU_ONLINE:
+ case CPU_DOWN_FAILED:
+ case CPU_ONLINE_FROZEN:
+ irq_set_affinity(s5p_ehci_irq_no, cpumask_of(s5p_ehci_irq_cpu));
+ pr_debug("%s: set ehci irq to cpu%d\n", __func__, cpu);
+ break;
+ case CPU_DOWN_PREPARE:
+ case CPU_DOWN_PREPARE_FROZEN:
+ irq_set_affinity(s5p_ehci_irq_no, cpumask_of(0));
+ pr_debug("%s: set ehci irq to cpu%d\n", __func__, 0);
+ break;
+ default:
+ break;
+ }
+exit:
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata s5p_ehci_cpu_notifier = {
+ .notifier_call = s5p_ehci_cpu_notify,
+};
+#endif
+
static int __devinit s5p_ehci_probe(struct platform_device *pdev)
{
struct s5p_ehci_platdata *pdata;
@@ -590,6 +699,27 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev)
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
#endif
+#ifdef CONFIG_MDM_HSIC_PM
+ /* halt controller before driving suspend on ths bus */
+ ehci->susp_sof_bug = 1;
+
+ set_host_stat(hsic_pm_dev, POWER_ON);
+ pm_runtime_allow(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&hcd->self.root_hub->dev, 0);
+
+ pm_runtime_forbid(&pdev->dev);
+ enable_periodic(ehci);
+#endif
+
+#ifdef CONFIG_EHCI_IRQ_DISTRIBUTION
+ if (num_possible_cpus() > 1) {
+ s5p_ehci_irq_no = irq;
+ s5p_ehci_irq_cpu = s5p_ehci_cpus[num_possible_cpus() - 1];
+ irq_set_affinity(s5p_ehci_irq_no, cpumask_of(s5p_ehci_irq_cpu));
+ register_cpu_notifier(&s5p_ehci_cpu_notifier);
+ }
+#endif
+
#if defined(CONFIG_LINK_DEVICE_HSIC) || defined(CONFIG_LINK_DEVICE_USB)
/* for cp enumeration */
pm_runtime_forbid(&pdev->dev);
@@ -619,13 +749,30 @@ static int __devexit s5p_ehci_remove(struct platform_device *pdev)
struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev);
struct usb_hcd *hcd = s5p_ehci->hcd;
+/* pm_runtime_disable called twice during pdev unregistering
+ * it causes disable_depth mismatching, so rpm for this device
+ * cannot works from disable_depth count
+ * replace it to runtime forbid.
+ */
#ifdef CONFIG_USB_SUSPEND
+#ifdef CONFIG_MDM_HSIC_PM
+ pm_runtime_forbid(&pdev->dev);
+#else
pm_runtime_disable(&pdev->dev);
#endif
+#endif
s5p_ehci->power_on = 0;
remove_ehci_sys_file(hcd_to_ehci(hcd));
usb_remove_hcd(hcd);
+#ifdef CONFIG_EHCI_IRQ_DISTRIBUTION
+ if (num_possible_cpus() > 1) {
+ s5p_ehci_irq_no = 0;
+ s5p_ehci_irq_cpu = 0;
+ unregister_cpu_notifier(&s5p_ehci_cpu_notifier);
+ }
+#endif
+
#if defined(CONFIG_LINK_DEVICE_HSIC) || defined(CONFIG_LINK_DEVICE_USB)
/*HSIC IPC control the ACTIVE_STATE*/
if (pdata && pdata->noti_host_states)