From a4e97fb2370ee832e0e9836fc46b3645056a41cf Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 18 Nov 2010 20:24:24 -0800 Subject: [PATCH 1/6] tegra: SDHCI: disable clock at suspend Ensure sdmmc clock is disabled at suspend, and that the subsequent resume will perform clock enable processing, which is required if LP0 suspend is in effect. Change-Id: Ia98698f13cc557040e5fdbc523a97effb63ac63b Signed-off-by: Todd Poynor --- drivers/mmc/host/sdhci-tegra.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 19f522d3a59f..2cb479987b8d 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -206,6 +206,7 @@ static int tegra_sdhci_suspend(struct platform_device *pdev, pm_message_t state) if (ret) pr_err("%s: failed, error = %d\n", __func__, ret); + tegra_sdhci_enable_clock(host, 0); return ret; } From d340f9224dbf9be5d597c18222073c9d328b3473 Mon Sep 17 00:00:00 2001 From: Robert Morell Date: Fri, 19 Nov 2010 18:55:51 -0800 Subject: [PATCH 2/6] video: tegra: Scale colors in pseudopalette This fixes incorrect colors drawn by clients such as fbconsole. As described by skeletonfb's fb_setcolreg: "The values supplied have a 16 bit magnitude which needs to be scaled in this function for the hardware." Tested with both r8g8b8 and r5g6b5 pixel depths and fbconsole. Change-Id: Ie3c3579502ddab8843a8a4dc7049c6efaa5d0ac1 Signed-off-by: Robert Morell Acked-by: Erik Gilling --- drivers/video/tegra/fb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c index 15a4f5b472a8..cc26c5977a20 100644 --- a/drivers/video/tegra/fb.c +++ b/drivers/video/tegra/fb.c @@ -198,6 +198,10 @@ static int tegra_fb_setcolreg(unsigned regno, unsigned red, unsigned green, if (regno >= 16) return -EINVAL; + red = (red >> (16 - info->var.red.length)); + green = (green >> (16 - info->var.green.length)); + blue = (blue >> (16 - info->var.blue.length)); + v = (red << var->red.offset) | (green << var->green.offset) | (blue << var->blue.offset); From d6f7633ef29cce12e4b7526f78286f2c8c818219 Mon Sep 17 00:00:00 2001 From: Robert Morell Date: Fri, 19 Nov 2010 19:17:36 -0800 Subject: [PATCH 3/6] video: tegra: host: Fix bounds-checking The length of the mod_locks array is NV_HOST1X_NB_MLOCKS, not NV_HOST1X_SYNCPT_NB_PTS. Change-Id: Ibce054bb8a168f2b83646745f2b62cd282b8ff9d Signed-off-by: Robert Morell Acked-by: Erik Gilling --- drivers/video/tegra/host/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c index daed882be5a2..42d268e7da59 100644 --- a/drivers/video/tegra/host/dev.c +++ b/drivers/video/tegra/host/dev.c @@ -452,7 +452,7 @@ static int nvhost_ioctl_ctrl_module_mutex( struct nvhost_ctrl_module_mutex_args *args) { int err = 0; - if (args->id >= NV_HOST1X_SYNCPT_NB_PTS || + if (args->id >= NV_HOST1X_NB_MLOCKS || args->lock > 1) return -EINVAL; From fa43ad045250d900f079ad4f8c6a51014143ae3a Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Wed, 17 Nov 2010 14:27:51 -0800 Subject: [PATCH 4/6] usb: gadget: rndis: don't use dev_get_stats Since 2.6.36, spin_lock_bh has been added to dev_txq_stats_fold. So dev_get_stats cannot be called from atomic context. Replaced it by netdev_stats_to_stats64 to copy the stats directly. This will work if the device driver does not need txq tx stats folding. http://lkml.org/lkml/2010/9/20/250 Change-Id: I715b00892beda56cc369139d6e2bdc9efb6bfe79 Signed-off-by: Benoit Goby --- drivers/usb/gadget/rndis.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 972d5ddd1e18..116347b0bf13 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -159,6 +159,25 @@ static const u32 oid_supported_list [] = #endif /* RNDIS_PM */ }; +/* HACK: copied from net/core/dev.c to replace dev_get_stats since + * dev_get_stats cannot be called from atomic context */ +static void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, + const struct net_device_stats *netdev_stats) +{ +#if BITS_PER_LONG == 64 + BUILD_BUG_ON(sizeof(*stats64) != sizeof(*netdev_stats)); + memcpy(stats64, netdev_stats, sizeof(*stats64)); +#else + size_t i, n = sizeof(*stats64) / sizeof(u64); + const unsigned long *src = (const unsigned long *)netdev_stats; + u64 *dst = (u64 *)stats64; + + BUILD_BUG_ON(sizeof(*netdev_stats) / sizeof(unsigned long) != + sizeof(*stats64) / sizeof(u64)); + for (i = 0; i < n; i++) + dst[i] = src[i]; +#endif +} /* NDIS Functions */ static int @@ -172,7 +191,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, rndis_query_cmplt_type *resp; struct net_device *net; struct rtnl_link_stats64 temp; - const struct rtnl_link_stats64 *stats; + struct rtnl_link_stats64 *stats = &temp; if (!r) return -ENOMEM; resp = (rndis_query_cmplt_type *) r->buf; @@ -195,7 +214,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, resp->InformationBufferOffset = cpu_to_le32 (16); net = rndis_per_dev_params[configNr].dev; - stats = dev_get_stats(net, &temp); + netdev_stats_to_stats64(stats, &net->stats); switch (OID) { From c5436143600aadf50375658894c258fb74435b37 Mon Sep 17 00:00:00 2001 From: Jay Cheng Date: Mon, 22 Nov 2010 11:03:52 -0500 Subject: [PATCH 5/6] ARM: tegra: usb_phy: Disable disconnect detection during resume Change-Id: I30a45dc7e7a87773a93c128877d0f0827e5d44b7 Signed-off-by: Jay Cheng --- arch/arm/mach-tegra/include/mach/usb_phy.h | 4 +++ arch/arm/mach-tegra/usb_phy.c | 36 ++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h index 71f1b9fa76db..8b4beae773a7 100644 --- a/arch/arm/mach-tegra/include/mach/usb_phy.h +++ b/arch/arm/mach-tegra/include/mach/usb_phy.h @@ -79,6 +79,10 @@ int tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy); int tegra_usb_phy_power_off(struct tegra_usb_phy *phy); +int tegra_usb_phy_preresume(struct tegra_usb_phy *phy); + +int tegra_usb_phy_postresume(struct tegra_usb_phy *phy); + int tegra_usb_phy_close(struct tegra_usb_phy *phy); #endif //__MACH_USB_PHY_H diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c index dffabc3ec063..26605d9c2c0f 100644 --- a/arch/arm/mach-tegra/usb_phy.c +++ b/arch/arm/mach-tegra/usb_phy.c @@ -347,8 +347,6 @@ static void utmi_phy_power_on(struct tegra_usb_phy *phy) val = readl(base + UTMIP_TX_CFG0); val &= ~UTMIP_FS_PREABMLE_J; - if (phy->instance == 2) - val |= UTMIP_HS_DISCON_DISABLE; writel(val, base + UTMIP_TX_CFG0); val = readl(base + UTMIP_HSRX_CFG0); @@ -481,6 +479,26 @@ static void utmi_phy_power_off(struct tegra_usb_phy *phy) utmip_pad_power_off(phy); } +static void utmi_phy_preresume(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + val = readl(base + UTMIP_TX_CFG0); + val |= UTMIP_HS_DISCON_DISABLE; + writel(val, base + UTMIP_TX_CFG0); +} + +static void utmi_phy_postresume(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + val = readl(base + UTMIP_TX_CFG0); + val &= ~UTMIP_HS_DISCON_DISABLE; + writel(val, base + UTMIP_TX_CFG0); +} + static void ulpi_viewport_write(struct tegra_usb_phy *phy, u8 addr, u8 data) { unsigned long val; @@ -672,6 +690,20 @@ int tegra_usb_phy_power_off(struct tegra_usb_phy *phy) return 0; } +int tegra_usb_phy_preresume(struct tegra_usb_phy *phy) +{ + if (phy->instance == 2) + utmi_phy_preresume(phy); + return 0; +} + +int tegra_usb_phy_postresume(struct tegra_usb_phy *phy) +{ + if (phy->instance == 2) + utmi_phy_postresume(phy); + return 0; +} + int tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy) { if (phy->instance != 1) From ea1a88753b2c59ec2924418234d34ec1a23f80e0 Mon Sep 17 00:00:00 2001 From: Jay Cheng Date: Mon, 22 Nov 2010 11:03:52 -0500 Subject: [PATCH 6/6] usb: host: tegra: rewrite usb suspend/resume routine. Tegra host controller will time the resume operation to clear the bit when the port control state switches to HS or FS Idle. This behavior is different from EHCI where the host controller driver is required to set this bit to a zero after the resume duration is timed in the driver. Poll PORT_SUSPEND bit till the suspend is completed. Write PORT_RESUME to 0 to clear PORT_SUSPEND bit. Disable disconnect detection during resume. Change-Id: I30a45dc7e7a87773a93c128877d0f0827e5d44b7 Signed-off-by: Jay Cheng --- drivers/usb/host/ehci-tegra.c | 85 ++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index e566a97f21dc..e3b01b39fc3f 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -50,6 +50,7 @@ struct tegra_ehci_hcd { struct otg_transceiver *transceiver; int host_resumed; int bus_suspended; + int port_resuming; struct tegra_ehci_context context; int power_down_on_bus_suspend; }; @@ -82,6 +83,7 @@ static int tegra_ehci_hub_control( ) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); u32 __iomem *status_reg; u32 temp; unsigned long flags; @@ -94,7 +96,7 @@ static int tegra_ehci_hub_control( * that are write on clear, by writing back the register read value, so * USB_PORT_FEAT_ENABLE is handled by masking the set on clear bits */ - if ((typeReq == ClearPortFeature) && (wValue == USB_PORT_FEAT_ENABLE)) { + if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_ENABLE) { spin_lock_irqsave(&ehci->lock, flags); temp = ehci_readl(ehci, status_reg); ehci_writel(ehci, (temp & ~PORT_RWC_BITS) & ~PORT_PE, status_reg); @@ -102,6 +104,85 @@ static int tegra_ehci_hub_control( return retval; } + if (typeReq == GetPortStatus) { + spin_lock_irqsave(&ehci->lock, flags); + temp = ehci_readl(ehci, status_reg); + if (tegra->port_resuming && !(temp & PORT_SUSPEND)) { + /* resume completed */ + tegra->port_resuming = 0; + tegra_usb_phy_postresume(tegra->phy); + } + spin_unlock_irqrestore(&ehci->lock, flags); + } + + if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) { + spin_lock_irqsave(&ehci->lock, flags); + temp = ehci_readl(ehci, status_reg); + if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) + retval = -EPIPE; + + /* After above check the port must be connected. + * Set appropriate bit thus could put phy into low power + * mode if we have hostpc feature + */ + temp &= ~PORT_WKCONN_E; + temp |= PORT_WKDISC_E | PORT_WKOC_E; + ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); + if (handshake(ehci, status_reg, PORT_SUSPEND, + PORT_SUSPEND, 5000)) + pr_err("%s: timeout waiting for PORT_SUSPEND\n", __func__); + spin_unlock_irqrestore(&ehci->lock, flags); + return retval; + } + + /* + * Tegra host controller will time the resume operation to clear the bit + * when the port control state switches to HS or FS Idle. This behavior + * is different from EHCI where the host controller driver is required + * to set this bit to a zero after the resume duration is timed in the + * driver. + */ + if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_SUSPEND) { + spin_lock_irqsave(&ehci->lock, flags); + temp = ehci_readl(ehci, status_reg); + if ((temp & PORT_RESET) || !(temp & PORT_PE)) + retval = -EPIPE; + + if (!(temp & PORT_SUSPEND)) { + spin_unlock_irqrestore(&ehci->lock, flags); + return retval; + } + + tegra_usb_phy_preresume(tegra->phy); + + /* reschedule root hub polling during resume signaling */ + ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25); + /* check the port again */ + mod_timer(&ehci_to_hcd(ehci)->rh_timer, + ehci->reset_done[wIndex-1]); + + temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); + /* start resume signalling */ + ehci_writel(ehci, temp | PORT_RESUME, status_reg); + + /* polling PORT_RESUME until the controller clear this bit */ + if (handshake(ehci, status_reg, PORT_RESUME, 0, 20000)) + pr_err("%s: timeout waiting for PORT_RESUME\n", __func__); + + /* write PORT_RESUME to 0 to clear PORT_SUSPEND bit */ + temp &= ~(PORT_RESUME | PORT_SUSPEND); + ehci_writel(ehci, temp, status_reg); + + /* polling PORT_SUSPEND until the controller clear this bit */ + if (handshake(ehci, status_reg, PORT_SUSPEND, 0, 2000)) + pr_err("%s: timeout waiting for PORT_SUSPEND\n", __func__); + + tegra->port_resuming = 1; + + spin_unlock_irqrestore(&ehci->lock, flags); + return retval; + } + /* Handle the hub control events here */ return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); } @@ -357,6 +438,8 @@ static int tegra_ehci_bus_resume(struct usb_hcd *hcd) tegra->bus_suspended = 0; } + tegra_usb_phy_preresume(tegra->phy); + tegra->port_resuming = 1; return ehci_bus_resume(hcd); }