diff options
author | Tuomas Tynkkynen <ttynkkynen@nvidia.com> | 2013-08-12 09:06:51 -0400 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2013-08-12 14:29:48 -0400 |
commit | 3e635202ce40e4d7ff3fafc18db70c5d28cc6622 (patch) | |
tree | 9feaee838221202b9a87e5ae49e6f94f212f17b7 /drivers/usb/phy/phy-tegra-usb.c | |
parent | f5833a0bde5d7795b19f8a881278e5506ab5764b (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.c | 121 |
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 | |||
144 | static DEFINE_SPINLOCK(utmip_pad_lock); | 156 | static DEFINE_SPINLOCK(utmip_pad_lock); |
145 | static int utmip_pad_count; | 157 | static 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 | ||
202 | static void set_phcd(struct tegra_usb_phy *phy, bool enable) | 221 | static 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 | ||
215 | static int utmip_pad_open(struct tegra_usb_phy *phy) | 243 | static 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 | ||
909 | static 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 | |||
916 | static 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 | |||
923 | static 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 | }; | ||
928 | MODULE_DEVICE_TABLE(of, tegra_usb_phy_id_table); | ||
929 | |||
867 | static int tegra_usb_phy_probe(struct platform_device *pdev) | 930 | static 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 | ||
971 | static struct of_device_id tegra_usb_phy_id_table[] = { | ||
972 | { .compatible = "nvidia,tegra20-usb-phy", }, | ||
973 | { }, | ||
974 | }; | ||
975 | MODULE_DEVICE_TABLE(of, tegra_usb_phy_id_table); | ||
976 | |||
977 | static struct platform_driver tegra_usb_phy_driver = { | 1042 | static 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, |