aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/phy/phy-tegra-usb.c
diff options
context:
space:
mode:
authorTuomas Tynkkynen <ttynkkynen@nvidia.com>2013-08-12 09:06:51 -0400
committerFelipe Balbi <balbi@ti.com>2013-08-12 14:29:48 -0400
commit3e635202ce40e4d7ff3fafc18db70c5d28cc6622 (patch)
tree9feaee838221202b9a87e5ae49e6f94f212f17b7 /drivers/usb/phy/phy-tegra-usb.c
parentf5833a0bde5d7795b19f8a881278e5506ab5764b (diff)
usb: phy: tegra: Tegra30 support
The Tegra30 USB PHY is a bit different than the Tegra20 PHY: - The EHCI controller supports the HOSTPC register extension, and some of the fields that the PHY needs to modify (PHCD and PTS) have moved to the new HOSTPC register. - Some of the UTMI PLL configuration registers have moved from the USB register space to the Clock-And-Reset controller space. In Tegra30 the clock driver is responsible for configuring the UTMI PLL. - The USBMODE register must be explicitly written to enter host mode. - Certain PHY parameters need to be programmed for optimal signal quality. Support for this will be added in the next patch. The new tegra_phy_soc_config structure is added to describe the differences between the SoCs. Signed-off-by: Tuomas Tynkkynen <ttynkkynen@nvidia.com> Tested-by: Stephen Warren <swarren@nvidia.com> Reviewed-by: Stephen Warren <swarren@nvidia.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/phy/phy-tegra-usb.c')
-rw-r--r--drivers/usb/phy/phy-tegra-usb.c121
1 files changed, 93 insertions, 28 deletions
diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c
index ebbf85fdfc7f..1ad184af7601 100644
--- a/drivers/usb/phy/phy-tegra-usb.c
+++ b/drivers/usb/phy/phy-tegra-usb.c
@@ -28,6 +28,7 @@
28#include <linux/io.h> 28#include <linux/io.h>
29#include <linux/gpio.h> 29#include <linux/gpio.h>
30#include <linux/of.h> 30#include <linux/of.h>
31#include <linux/of_device.h>
31#include <linux/of_gpio.h> 32#include <linux/of_gpio.h>
32#include <linux/usb/otg.h> 33#include <linux/usb/otg.h>
33#include <linux/usb/ulpi.h> 34#include <linux/usb/ulpi.h>
@@ -39,11 +40,16 @@
39 40
40#define ULPI_VIEWPORT 0x170 41#define ULPI_VIEWPORT 0x170
41 42
42/* PORTSC registers */ 43/* PORTSC PTS/PHCD bits, Tegra20 only */
43#define TEGRA_USB_PORTSC1 0x184 44#define TEGRA_USB_PORTSC1 0x184
44#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30) 45#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30)
45#define TEGRA_USB_PORTSC1_PHCD (1 << 23) 46#define TEGRA_USB_PORTSC1_PHCD (1 << 23)
46 47
48/* HOSTPC1 PTS/PHCD bits, Tegra30 and above */
49#define TEGRA_USB_HOSTPC1_DEVLC 0x1b4
50#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29)
51#define TEGRA_USB_HOSTPC1_DEVLC_PHCD (1 << 22)
52
47/* Bits of PORTSC1, which will get cleared by writing 1 into them */ 53/* Bits of PORTSC1, which will get cleared by writing 1 into them */
48#define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) 54#define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
49 55
@@ -141,6 +147,12 @@
141#define UTMIP_BIAS_CFG1 0x83c 147#define UTMIP_BIAS_CFG1 0x83c
142#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3) 148#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3)
143 149
150/* For Tegra30 and above only, the address is different in Tegra20 */
151#define USB_USBMODE 0x1f8
152#define USB_USBMODE_MASK (3 << 0)
153#define USB_USBMODE_HOST (3 << 0)
154#define USB_USBMODE_DEVICE (2 << 0)
155
144static DEFINE_SPINLOCK(utmip_pad_lock); 156static DEFINE_SPINLOCK(utmip_pad_lock);
145static int utmip_pad_count; 157static int utmip_pad_count;
146 158
@@ -193,10 +205,17 @@ static void set_pts(struct tegra_usb_phy *phy, u8 pts_val)
193 void __iomem *base = phy->regs; 205 void __iomem *base = phy->regs;
194 unsigned long val; 206 unsigned long val;
195 207
196 val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS; 208 if (phy->soc_config->has_hostpc) {
197 val &= ~TEGRA_USB_PORTSC1_PTS(3); 209 val = readl(base + TEGRA_USB_HOSTPC1_DEVLC);
198 val |= TEGRA_USB_PORTSC1_PTS(pts_val & 3); 210 val &= ~TEGRA_USB_HOSTPC1_DEVLC_PTS(~0);
199 writel(val, base + TEGRA_USB_PORTSC1); 211 val |= TEGRA_USB_HOSTPC1_DEVLC_PTS(pts_val);
212 writel(val, base + TEGRA_USB_HOSTPC1_DEVLC);
213 } else {
214 val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS;
215 val &= ~TEGRA_USB_PORTSC1_PTS(~0);
216 val |= TEGRA_USB_PORTSC1_PTS(pts_val);
217 writel(val, base + TEGRA_USB_PORTSC1);
218 }
200} 219}
201 220
202static void set_phcd(struct tegra_usb_phy *phy, bool enable) 221static void set_phcd(struct tegra_usb_phy *phy, bool enable)
@@ -204,12 +223,21 @@ static void set_phcd(struct tegra_usb_phy *phy, bool enable)
204 void __iomem *base = phy->regs; 223 void __iomem *base = phy->regs;
205 unsigned long val; 224 unsigned long val;
206 225
207 val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS; 226 if (phy->soc_config->has_hostpc) {
208 if (enable) 227 val = readl(base + TEGRA_USB_HOSTPC1_DEVLC);
209 val |= TEGRA_USB_PORTSC1_PHCD; 228 if (enable)
210 else 229 val |= TEGRA_USB_HOSTPC1_DEVLC_PHCD;
211 val &= ~TEGRA_USB_PORTSC1_PHCD; 230 else
212 writel(val, base + TEGRA_USB_PORTSC1); 231 val &= ~TEGRA_USB_HOSTPC1_DEVLC_PHCD;
232 writel(val, base + TEGRA_USB_HOSTPC1_DEVLC);
233 } else {
234 val = readl(base + TEGRA_USB_PORTSC1) & ~PORT_RWC_BITS;
235 if (enable)
236 val |= TEGRA_USB_PORTSC1_PHCD;
237 else
238 val &= ~TEGRA_USB_PORTSC1_PHCD;
239 writel(val, base + TEGRA_USB_PORTSC1);
240 }
213} 241}
214 242
215static int utmip_pad_open(struct tegra_usb_phy *phy) 243static int utmip_pad_open(struct tegra_usb_phy *phy)
@@ -367,17 +395,21 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
367 val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE; 395 val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE;
368 writel(val, base + UTMIP_MISC_CFG0); 396 writel(val, base + UTMIP_MISC_CFG0);
369 397
370 val = readl(base + UTMIP_MISC_CFG1); 398 if (!phy->soc_config->utmi_pll_config_in_car_module) {
371 val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) | UTMIP_PLLU_STABLE_COUNT(~0)); 399 val = readl(base + UTMIP_MISC_CFG1);
372 val |= UTMIP_PLL_ACTIVE_DLY_COUNT(phy->freq->active_delay) | 400 val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) |
373 UTMIP_PLLU_STABLE_COUNT(phy->freq->stable_count); 401 UTMIP_PLLU_STABLE_COUNT(~0));
374 writel(val, base + UTMIP_MISC_CFG1); 402 val |= UTMIP_PLL_ACTIVE_DLY_COUNT(phy->freq->active_delay) |
375 403 UTMIP_PLLU_STABLE_COUNT(phy->freq->stable_count);
376 val = readl(base + UTMIP_PLL_CFG1); 404 writel(val, base + UTMIP_MISC_CFG1);
377 val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) | UTMIP_PLLU_ENABLE_DLY_COUNT(~0)); 405
378 val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) | 406 val = readl(base + UTMIP_PLL_CFG1);
379 UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay); 407 val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) |
380 writel(val, base + UTMIP_PLL_CFG1); 408 UTMIP_PLLU_ENABLE_DLY_COUNT(~0));
409 val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) |
410 UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
411 writel(val, base + UTMIP_PLL_CFG1);
412 }
381 413
382 if (phy->mode == USB_DR_MODE_PERIPHERAL) { 414 if (phy->mode == USB_DR_MODE_PERIPHERAL) {
383 val = readl(base + USB_SUSP_CTRL); 415 val = readl(base + USB_SUSP_CTRL);
@@ -448,6 +480,16 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
448 480
449 utmi_phy_clk_enable(phy); 481 utmi_phy_clk_enable(phy);
450 482
483 if (phy->soc_config->requires_usbmode_setup) {
484 val = readl(base + USB_USBMODE);
485 val &= ~USB_USBMODE_MASK;
486 if (phy->mode == USB_DR_MODE_HOST)
487 val |= USB_USBMODE_HOST;
488 else
489 val |= USB_USBMODE_DEVICE;
490 writel(val, base + USB_USBMODE);
491 }
492
451 if (!phy->is_legacy_phy) 493 if (!phy->is_legacy_phy)
452 set_pts(phy, 0); 494 set_pts(phy, 0);
453 495
@@ -864,8 +906,30 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy,
864 return 0; 906 return 0;
865} 907}
866 908
909static const struct tegra_phy_soc_config tegra20_soc_config = {
910 .utmi_pll_config_in_car_module = false,
911 .has_hostpc = false,
912 .requires_usbmode_setup = false,
913 .requires_extra_tuning_parameters = false,
914};
915
916static const struct tegra_phy_soc_config tegra30_soc_config = {
917 .utmi_pll_config_in_car_module = true,
918 .has_hostpc = true,
919 .requires_usbmode_setup = true,
920 .requires_extra_tuning_parameters = true,
921};
922
923static struct of_device_id tegra_usb_phy_id_table[] = {
924 { .compatible = "nvidia,tegra30-usb-phy", .data = &tegra30_soc_config },
925 { .compatible = "nvidia,tegra20-usb-phy", .data = &tegra20_soc_config },
926 { },
927};
928MODULE_DEVICE_TABLE(of, tegra_usb_phy_id_table);
929
867static int tegra_usb_phy_probe(struct platform_device *pdev) 930static int tegra_usb_phy_probe(struct platform_device *pdev)
868{ 931{
932 const struct of_device_id *match;
869 struct resource *res; 933 struct resource *res;
870 struct tegra_usb_phy *tegra_phy = NULL; 934 struct tegra_usb_phy *tegra_phy = NULL;
871 struct device_node *np = pdev->dev.of_node; 935 struct device_node *np = pdev->dev.of_node;
@@ -878,6 +942,13 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
878 return -ENOMEM; 942 return -ENOMEM;
879 } 943 }
880 944
945 match = of_match_device(tegra_usb_phy_id_table, &pdev->dev);
946 if (!match) {
947 dev_err(&pdev->dev, "Error: No device match found\n");
948 return -ENODEV;
949 }
950 tegra_phy->soc_config = match->data;
951
881 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 952 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
882 if (!res) { 953 if (!res) {
883 dev_err(&pdev->dev, "Failed to get I/O memory\n"); 954 dev_err(&pdev->dev, "Failed to get I/O memory\n");
@@ -968,12 +1039,6 @@ static int tegra_usb_phy_remove(struct platform_device *pdev)
968 return 0; 1039 return 0;
969} 1040}
970 1041
971static struct of_device_id tegra_usb_phy_id_table[] = {
972 { .compatible = "nvidia,tegra20-usb-phy", },
973 { },
974};
975MODULE_DEVICE_TABLE(of, tegra_usb_phy_id_table);
976
977static struct platform_driver tegra_usb_phy_driver = { 1042static struct platform_driver tegra_usb_phy_driver = {
978 .probe = tegra_usb_phy_probe, 1043 .probe = tegra_usb_phy_probe,
979 .remove = tegra_usb_phy_remove, 1044 .remove = tegra_usb_phy_remove,