aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/ehci-hcd.c27
-rw-r--r--drivers/usb/host/ehci-hub.c57
-rw-r--r--drivers/usb/host/ehci-q.c18
-rw-r--r--drivers/usb/host/ehci-s5p.c165
-rw-r--r--drivers/usb/host/ehci.h3
-rw-r--r--drivers/usb/host/ohci-s5p.c26
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;