diff options
Diffstat (limited to 'drivers/usb/host/ehci-tegra.c')
| -rw-r--r-- | drivers/usb/host/ehci-tegra.c | 97 | 
1 files changed, 65 insertions, 32 deletions
| diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index acf17556bd8..568aecc7075 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -2,7 +2,7 @@   * EHCI-compliant USB host controller driver for NVIDIA Tegra SoCs   *   * Copyright (C) 2010 Google, Inc. - * Copyright (C) 2009 NVIDIA Corporation + * Copyright (C) 2009 - 2013 NVIDIA Corporation   *   * This program is free software; you can redistribute it and/or modify it   * under the terms of the GNU General Public License as published by the @@ -26,23 +26,28 @@  #include <linux/of.h>  #include <linux/of_gpio.h>  #include <linux/pm_runtime.h> - +#include <linux/usb/ehci_def.h>  #include <linux/usb/tegra_usb_phy.h>  #define TEGRA_USB_BASE			0xC5000000  #define TEGRA_USB2_BASE			0xC5004000  #define TEGRA_USB3_BASE			0xC5008000 +/* PORTSC registers */ +#define TEGRA_USB_PORTSC1			0x184 +#define TEGRA_USB_PORTSC1_PTS(x)	(((x) & 0x3) << 30) +#define TEGRA_USB_PORTSC1_PHCD	(1 << 23) +  #define TEGRA_USB_DMA_ALIGN 32  struct tegra_ehci_hcd {  	struct ehci_hcd *ehci;  	struct tegra_usb_phy *phy;  	struct clk *clk; -	struct clk *emc_clk;  	struct usb_phy *transceiver;  	int host_resumed;  	int port_resuming; +	bool needs_double_reset;  	enum tegra_usb_phy_port_speed port_speed;  }; @@ -50,9 +55,8 @@ static void tegra_ehci_power_up(struct usb_hcd *hcd)  {  	struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); -	clk_prepare_enable(tegra->emc_clk);  	clk_prepare_enable(tegra->clk); -	usb_phy_set_suspend(&tegra->phy->u_phy, 0); +	usb_phy_set_suspend(hcd->phy, 0);  	tegra->host_resumed = 1;  } @@ -61,9 +65,8 @@ static void tegra_ehci_power_down(struct usb_hcd *hcd)  	struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);  	tegra->host_resumed = 0; -	usb_phy_set_suspend(&tegra->phy->u_phy, 1); +	usb_phy_set_suspend(hcd->phy, 1);  	clk_disable_unprepare(tegra->clk); -	clk_disable_unprepare(tegra->emc_clk);  }  static int tegra_ehci_internal_port_reset( @@ -156,7 +159,7 @@ static int tegra_ehci_hub_control(  		if (tegra->port_resuming && !(temp & PORT_SUSPEND)) {  			/* Resume completed, re-enable disconnect detection */  			tegra->port_resuming = 0; -			tegra_usb_phy_postresume(tegra->phy); +			tegra_usb_phy_postresume(hcd->phy);  		}  	} @@ -184,7 +187,7 @@ static int tegra_ehci_hub_control(  	}  	/* For USB1 port we need to issue Port Reset twice internally */ -	if (tegra->phy->instance == 0 && +	if (tegra->needs_double_reset &&  	   (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_RESET)) {  		spin_unlock_irqrestore(&ehci->lock, flags);  		return tegra_ehci_internal_port_reset(ehci, status_reg); @@ -209,7 +212,7 @@ static int tegra_ehci_hub_control(  			goto done;  		/* Disable disconnect detection during port resume */ -		tegra_usb_phy_preresume(tegra->phy); +		tegra_usb_phy_preresume(hcd->phy);  		ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25); @@ -473,7 +476,7 @@ static int controller_resume(struct device *dev)  	}  	/* Force the phy to keep data lines in suspend state */ -	tegra_ehci_phy_restore_start(tegra->phy, tegra->port_speed); +	tegra_ehci_phy_restore_start(hcd->phy, tegra->port_speed);  	/* Enable host mode */  	tdi_reset(ehci); @@ -540,17 +543,17 @@ static int controller_resume(struct device *dev)  		}  	} -	tegra_ehci_phy_restore_end(tegra->phy); +	tegra_ehci_phy_restore_end(hcd->phy);  	goto done;   restart:  	if (tegra->port_speed <= TEGRA_USB_PHY_PORT_SPEED_HIGH) -		tegra_ehci_phy_restore_end(tegra->phy); +		tegra_ehci_phy_restore_end(hcd->phy);  	tegra_ehci_restart(hcd);   done: -	tegra_usb_phy_preresume(tegra->phy); +	tegra_usb_phy_preresume(hcd->phy);  	tegra->port_resuming = 1;  	return 0;  } @@ -604,6 +607,37 @@ static const struct dev_pm_ops tegra_ehci_pm_ops = {  #endif +/* Bits of PORTSC1, which will get cleared by writing 1 into them */ +#define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) + +void tegra_ehci_set_pts(struct usb_phy *x, u8 pts_val) +{ +	unsigned long val; +	struct usb_hcd *hcd = bus_to_hcd(x->otg->host); +	void __iomem *base = hcd->regs; + +	val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS; +	val &= ~TEGRA_USB_PORTSC1_PTS(3); +	val |= TEGRA_USB_PORTSC1_PTS(pts_val & 3); +	writel(val, base + TEGRA_USB_PORTSC1); +} +EXPORT_SYMBOL_GPL(tegra_ehci_set_pts); + +void tegra_ehci_set_phcd(struct usb_phy *x, bool enable) +{ +	unsigned long val; +	struct usb_hcd *hcd = bus_to_hcd(x->otg->host); +	void __iomem *base = hcd->regs; + +	val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS; +	if (enable) +		val |= TEGRA_USB_PORTSC1_PHCD; +	else +		val &= ~TEGRA_USB_PORTSC1_PHCD; +	writel(val, base + TEGRA_USB_PORTSC1); +} +EXPORT_SYMBOL_GPL(tegra_ehci_set_phcd); +  static u64 tegra_ehci_dma_mask = DMA_BIT_MASK(32);  static int tegra_ehci_probe(struct platform_device *pdev) @@ -615,6 +649,7 @@ static int tegra_ehci_probe(struct platform_device *pdev)  	int err = 0;  	int irq;  	int instance = pdev->id; +	struct usb_phy *u_phy;  	pdata = pdev->dev.platform_data;  	if (!pdata) { @@ -656,15 +691,8 @@ static int tegra_ehci_probe(struct platform_device *pdev)  	if (err)  		goto fail_clk; -	tegra->emc_clk = devm_clk_get(&pdev->dev, "emc"); -	if (IS_ERR(tegra->emc_clk)) { -		dev_err(&pdev->dev, "Can't get emc clock\n"); -		err = PTR_ERR(tegra->emc_clk); -		goto fail_emc_clk; -	} - -	clk_prepare_enable(tegra->emc_clk); -	clk_set_rate(tegra->emc_clk, 400000000); +	tegra->needs_double_reset = of_property_read_bool(pdev->dev.of_node, +		"nvidia,needs-double-reset");  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	if (!res) { @@ -712,9 +740,19 @@ static int tegra_ehci_probe(struct platform_device *pdev)  		goto fail_io;  	} -	usb_phy_init(&tegra->phy->u_phy); +	hcd->phy = u_phy = &tegra->phy->u_phy; +	usb_phy_init(hcd->phy); -	err = usb_phy_set_suspend(&tegra->phy->u_phy, 0); +	u_phy->otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg), +			     GFP_KERNEL); +	if (!u_phy->otg) { +		dev_err(&pdev->dev, "Failed to alloc memory for otg\n"); +		err = -ENOMEM; +		goto fail_io; +	} +	u_phy->otg->host = hcd_to_bus(hcd); + +	err = usb_phy_set_suspend(hcd->phy, 0);  	if (err) {  		dev_err(&pdev->dev, "Failed to power on the phy\n");  		goto fail; @@ -760,10 +798,8 @@ fail:  	if (!IS_ERR_OR_NULL(tegra->transceiver))  		otg_set_host(tegra->transceiver->otg, NULL);  #endif -	usb_phy_shutdown(&tegra->phy->u_phy); +	usb_phy_shutdown(hcd->phy);  fail_io: -	clk_disable_unprepare(tegra->emc_clk); -fail_emc_clk:  	clk_disable_unprepare(tegra->clk);  fail_clk:  	usb_put_hcd(hcd); @@ -784,15 +820,12 @@ static int tegra_ehci_remove(struct platform_device *pdev)  		otg_set_host(tegra->transceiver->otg, NULL);  #endif +	usb_phy_shutdown(hcd->phy);  	usb_remove_hcd(hcd);  	usb_put_hcd(hcd); -	usb_phy_shutdown(&tegra->phy->u_phy); -  	clk_disable_unprepare(tegra->clk); -	clk_disable_unprepare(tegra->emc_clk); -  	return 0;  } |