diff options
Diffstat (limited to 'drivers/usb/host/ehci-s5p.c')
-rw-r--r-- | drivers/usb/host/ehci-s5p.c | 165 |
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) |