diff options
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 27 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 57 | ||||
-rw-r--r-- | drivers/usb/host/ehci-q.c | 18 | ||||
-rw-r--r-- | drivers/usb/host/ehci-s5p.c | 165 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 3 | ||||
-rw-r--r-- | drivers/usb/host/ohci-s5p.c | 26 |
6 files changed, 260 insertions, 36 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 50bb6e0..75636d6 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -527,8 +527,26 @@ static void ehci_stop (struct usb_hcd *hcd) /* root hub is shut down separately (first, when possible) */ spin_lock_irq (&ehci->lock); +#ifdef CONFIG_MDM_HSIC_PM + if (ehci->async) { + /* + * TODO: Observed that ehci->async next ptr is not + * NULL sometimes which leads to crash in mem_cleanup. + * Root cause is not yet known why this messup is + * happenning. + * The follwing workaround fixes the crash caused + * by this temporarily. + * check if async next ptr is not NULL and unlink + * explictly. + */ + if (ehci->async->qh_next.ptr != NULL) + start_unlink_async(ehci, ehci->async->qh_next.qh); + ehci_work(ehci); + } +#else if (ehci->async) ehci_work (ehci); +#endif spin_unlock_irq (&ehci->lock); ehci_mem_cleanup (ehci); @@ -881,6 +899,13 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) pstatus = ehci_readl(ehci, &ehci->regs->port_status[i]); +#ifdef CONFIG_MDM_HSIC_PM + /*set RS bit in case of remote wakeup*/ + if (ehci_is_TDI(ehci) && !(cmd & CMD_RUN) && + (pstatus & PORT_SUSPEND)) + ehci_writel(ehci, cmd | CMD_RUN, + &ehci->regs->command); +#endif if (pstatus & PORT_OWNER) continue; if (!(test_bit(i, &ehci->suspended_ports) && @@ -911,7 +936,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) */ #ifdef CONFIG_LINK_DEVICE_HSIC /* ensure suspend bit clear by adding 5 msec delay. */ - ehci->reset_done[i] = jiffies + msecs_to_jiffies(30); + ehci->reset_done[i] = jiffies + msecs_to_jiffies(50); #else ehci->reset_done[i] = jiffies + msecs_to_jiffies(25); #endif diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index fb0394b..037fd42 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -31,6 +31,14 @@ #define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E) +#if defined(CONFIG_EMI_ERROR_RECOVERY) +#define MDM_HSIC_PORT_NUM 2 +#define PORT_ENABLE_DISABLE (1 << 2) +#define PORT_ENABLE_DISABLE_CHANGE (1 << 3) +/* count reported port status change */ +static int portstatus_chg_cnt; +#endif + #ifdef CONFIG_PM static int ehci_hub_control( @@ -261,6 +269,12 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) if (t1 & PORT_OWNER) set_bit(port, &ehci->owned_ports); else if ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) { +#ifdef CONFIG_MDM_HSIC_PM + /*clear RS bit before setting SUSP bit + * and wait for HCH to get set*/ + if (ehci->susp_sof_bug) + ehci_halt(ehci); +#endif t2 |= PORT_SUSPEND; set_bit(port, &ehci->bus_suspended); } @@ -311,8 +325,12 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) if (ehci->bus_suspended) udelay(150); - /* turn off now-idle HC */ - ehci_halt (ehci); +#ifdef CONFIG_MDM_HSIC_PM + /*if this bit is set, controller is already haled*/ + if (!ehci->susp_sof_bug) +#endif + /* turn off now-idle HC */ + ehci_halt(ehci); hcd->state = HC_STATE_SUSPENDED; if (ehci->reclaim) @@ -1349,6 +1367,27 @@ static int ehci_hub_control ( if (status & ~0xffff) /* only if wPortChange is interesting */ #endif dbg_port (ehci, "GetStatus", wIndex + 1, temp); + +#if defined(CONFIG_EMI_ERROR_RECOVERY) + if (temp & PORT_ENABLE_DISABLE_CHANGE) { + temp = ehci_readl(ehci, status_reg); + ehci_dbg(ehci, "recovery port status %d +\n", temp); + + /* ignore 'Current Status Change', by writing 1 */ + temp &= ~PORT_ENABLE_DISABLE; + + /* clear 'Port Enable/Disable Change', by writng 1 */ + temp |= PORT_ENABLE_DISABLE_CHANGE; + + ehci_writel(ehci, temp, status_reg); + ehci_readl(ehci, status_reg); + + ehci_dbg(ehci, "recovery port status %d -\n", + ehci_readl(ehci, status_reg)); + + portstatus_chg_cnt++; + } +#endif put_unaligned_le32(status, buf); break; case SetHubFeature: @@ -1388,6 +1427,12 @@ static int ehci_hub_control ( if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) goto error; +#ifdef CONFIG_MDM_HSIC_PM + /*port gets suspended as part of bus suspend routine*/ + if (!ehci->susp_sof_bug) + ehci_writel(ehci, temp | PORT_SUSPEND, + status_reg); +#endif /* After above check the port must be connected. * Set appropriate bit thus could put phy into low power @@ -1395,7 +1440,13 @@ static int ehci_hub_control ( */ temp &= ~PORT_WKCONN_E; temp |= PORT_WKDISC_E | PORT_WKOC_E; - ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); +#ifdef CONFIG_MDM_HSIC_PM + if (ehci->susp_sof_bug) + ehci_writel(ehci, temp, status_reg); + else +#endif + ehci_writel(ehci, temp | PORT_SUSPEND, + status_reg); if (hostpc_reg) { spin_unlock_irqrestore(&ehci->lock, flags); msleep(5);/* 5ms for HCD enter low pwr mode */ diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 5aa7cec..666f051 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1002,12 +1002,6 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) head->qh_next.qh = qh; head->hw->hw_next = dma; - /* - * flush qh descriptor into memory immediately, - * see comments in qh_append_tds. - * */ - ehci_sync_mem(); - qh_get(qh); qh->xacterrs = 0; qh->qh_state = QH_STATE_LINKED; @@ -1095,18 +1089,6 @@ static struct ehci_qh *qh_append_tds ( wmb (); dummy->hw_token = token; - /* - * Writing to dma coherent buffer on ARM may - * be delayed to reach memory, so HC may not see - * hw_token of dummy qtd in time, which can cause - * the qtd transaction to be executed very late, - * and degrade performance a lot. ehci_sync_mem - * is added to flush 'token' immediatelly into - * memory, so that ehci can execute the transaction - * ASAP. - * */ - ehci_sync_mem(); - urb->hcpriv = qh_get (qh); } } 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) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index f3db2b3..5a923a9 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -138,6 +138,9 @@ struct ehci_hcd { /* one per controller */ unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/ unsigned has_synopsys_hc_bug:1; /* Synopsys HC */ unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */ +#ifdef CONFIG_MDM_HSIC_PM + unsigned susp_sof_bug; /*Chip Idea HC*/ +#endif /* required for usb32 quirk */ #define OHCI_CTRL_HCFS (3 << 6) diff --git a/drivers/usb/host/ohci-s5p.c b/drivers/usb/host/ohci-s5p.c index fce68ef..641e40c 100644 --- a/drivers/usb/host/ohci-s5p.c +++ b/drivers/usb/host/ohci-s5p.c @@ -191,6 +191,20 @@ static int ohci_hcd_s5p_drv_runtime_resume(struct device *dev) #define ohci_hcd_s5p_drv_runtime_resume NULL #endif +static int ohci_s5p_init(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret; + + ohci_dbg(ohci, "ohci_s5p_init, ohci:%p", ohci); + + ret = ohci_init(ohci); + if (ret < 0) + return ret; + + return 0; +} + static int ohci_s5p_start(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci(hcd); @@ -198,10 +212,6 @@ static int ohci_s5p_start(struct usb_hcd *hcd) ohci_dbg(ohci, "ohci_s5p_start, ohci:%p", ohci); - ret = ohci_init(ohci); - if (ret < 0) - return ret; - ret = ohci_run(ohci); if (ret < 0) { err("can't start %s", hcd->self.bus_name); @@ -220,6 +230,7 @@ static const struct hc_driver ohci_s5p_hc_driver = { .irq = ohci_irq, .flags = HCD_MEMORY|HCD_USB11, + .reset = ohci_s5p_init, .start = ohci_s5p_start, .stop = ohci_stop, .shutdown = ohci_shutdown, @@ -447,7 +458,12 @@ static void ohci_hcd_s5p_drv_shutdown(struct platform_device *pdev) { struct s5p_ohci_platdata *pdata = pdev->dev.platform_data; struct s5p_ohci_hcd *s5p_ohci = platform_get_drvdata(pdev); - struct usb_hcd *hcd = s5p_ohci->hcd; + struct usb_hcd *hcd; + + if (!pdata || !s5p_ohci) + return; + + hcd = s5p_ohci->hcd; if (!s5p_ohci->power_on) return; |