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 | |
| 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')
| -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; | ||
