diff options
author | Seshendra Gadagottu <sgadagottu@nvidia.com> | 2011-01-11 11:41:09 -0800 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2011-02-10 15:41:11 -0800 |
commit | 8df80194dc456a45fc8245de57bd9b46d2cf16a8 (patch) | |
tree | 78a310256698305e2ac7dae9df58b39fd5cacec7 /arch | |
parent | 57a3407736092650cbed228fdf9264e140378762 (diff) |
tegra hsic: Adding USB hsic driver support to K36
Adding hsic funtionality to USB2 instance.
Add the changes required for hsic functionality and
power management.
BUG 756184
Change-Id: Ife8a1fc6ea95b15f66d840b1565d858ee25d5ded
Reviewed-on: http://git-master/r/15192
Tested-by: Seshendra Gadagottu <sgadagottu@nvidia.com>
Reviewed-by: Michael Hsu <mhsu@nvidia.com>
Tested-by: Michael Hsu <mhsu@nvidia.com>
Reviewed-by: Shail Dave <sdave@nvidia.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-tegra/include/mach/usb_phy.h | 24 | ||||
-rw-r--r-- | arch/arm/mach-tegra/usb_phy.c | 341 |
2 files changed, 346 insertions, 19 deletions
diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h index 8cb7eb457ff3..30c0f57fee70 100644 --- a/arch/arm/mach-tegra/include/mach/usb_phy.h +++ b/arch/arm/mach-tegra/include/mach/usb_phy.h @@ -2,6 +2,7 @@ * arch/arm/mach-tegra/include/mach/usb_phy.h * * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2011 NVIDIA Corporation. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -33,8 +34,9 @@ struct tegra_utmip_config { }; enum tegra_ulpi_inf_type { - TEGRA_USB_LINK_ULPI = 0, - TEGRA_USB_NULL_ULPI, + TEGRA_USB_LINK_ULPI = 0, + TEGRA_USB_NULL_ULPI, + TEGRA_USB_UHSIC, }; struct tegra_ulpi_trimmer { @@ -53,6 +55,14 @@ struct tegra_ulpi_config { int (*postinit)(void); }; +struct tegra_uhsic_config { + u8 sync_start_delay; + u8 idle_wait_delay; + u8 term_range_adj; + u8 elastic_underrun_limit; + u8 elastic_overrun_limit; +}; + enum tegra_usb_phy_port_speed { TEGRA_USB_PHY_PORT_SPEED_FULL = 0, TEGRA_USB_PHY_PORT_SPEED_LOW, @@ -98,4 +108,12 @@ int tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy); int tegra_usb_phy_close(struct tegra_usb_phy *phy); -#endif //__MACH_USB_PHY_H +int tegra_usb_phy_bus_connect(struct tegra_usb_phy *phy); + +int tegra_usb_phy_bus_reset(struct tegra_usb_phy *phy); + +int tegra_usb_phy_bus_idle(struct tegra_usb_phy *phy); + +bool tegra_usb_phy_is_device_connected(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 9e809253170f..bcc5cc464eb6 100644 --- a/arch/arm/mach-tegra/usb_phy.c +++ b/arch/arm/mach-tegra/usb_phy.c @@ -2,6 +2,7 @@ * arch/arm/mach-tegra/usb_phy.c * * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2010 - 2011 NVIDIA Corporation * * Author: * Erik Gilling <konkers@google.com> @@ -31,8 +32,12 @@ #include <mach/pinmux.h> #include "gpio-names.h" +#define USB_USBCMD 0x140 +#define USB_USBCMD_RS (1 << 0) + #define USB_USBSTS 0x144 #define USB_USBSTS_PCI (1 << 2) +#define USB_USBSTS_HCH (1 << 12) #define ULPI_VIEWPORT 0x170 #define ULPI_WAKEUP (1 << 31) @@ -53,6 +58,7 @@ #define USB_PORTSC1_WKCN (1 << 20) #define USB_PORTSC1_PTC(x) (((x) & 0xf) << 16) #define USB_PORTSC1_PP (1 << 12) +#define USB_PORTSC1_LS(x) (((x) & 0x3) << 10) #define USB_PORTSC1_SUSP (1 << 7) #define USB_PORTSC1_PE (1 << 2) #define USB_PORTSC1_CCS (1 << 0) @@ -62,9 +68,10 @@ #define USB_WAKE_ON_DISCON_EN_DEV (1 << 4) #define USB_SUSP_CLR (1 << 5) #define USB_PHY_CLK_VALID (1 << 7) -#define UTMIP_RESET (1 << 11) -#define UHSIC_RESET (1 << 11) -#define UTMIP_PHY_ENABLE (1 << 12) +#define UTMIP_RESET (1 << 11) +#define UHSIC_RESET (1 << 11) +#define UTMIP_PHY_ENABLE (1 << 12) +#define UHSIC_PHY_ENABLE (1 << 12) #define ULPI_PHY_ENABLE (1 << 13) #define USB_SUSP_SET (1 << 14) #define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16) @@ -168,6 +175,55 @@ #define UTMIP_BIAS_CFG1 0x83c #define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3) +#define UHSIC_PLL_CFG0 0x800 + +#define UHSIC_PLL_CFG1 0x804 +#define UHSIC_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0) +#define UHSIC_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 14) + +#define UHSIC_HSRX_CFG0 0x808 +#define UHSIC_ELASTIC_UNDERRUN_LIMIT(x) (((x) & 0x1f) << 2) +#define UHSIC_ELASTIC_OVERRUN_LIMIT(x) (((x) & 0x1f) << 8) +#define UHSIC_IDLE_WAIT(x) (((x) & 0x1f) << 13) + +#define UHSIC_HSRX_CFG1 0x80c +#define UHSIC_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1) + +#define UHSIC_TX_CFG0 0x810 +#define UHSIC_HS_POSTAMBLE_OUTPUT_ENABLE (1 << 6) + +#define UHSIC_MISC_CFG0 0x814 +#define UHSIC_SUSPEND_EXIT_ON_EDGE (1 << 7) +#define UHSIC_DETECT_SHORT_CONNECT (1 << 8) +#define UHSIC_FORCE_XCVR_MODE (1 << 15) + +#define UHSIC_MISC_CFG1 0X818 +#define UHSIC_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 2) + +#define UHSIC_PADS_CFG0 0x81c +#define UHSIC_TX_RTUNEN 0xf000 +#define UHSIC_TX_RTUNE(x) (((x) & 0xf) << 12) + +#define UHSIC_PADS_CFG1 0x820 +#define UHSIC_PD_BG (1 << 2) +#define UHSIC_PD_TX (1 << 3) +#define UHSIC_PD_TRK (1 << 4) +#define UHSIC_PD_RX (1 << 5) +#define UHSIC_PD_ZI (1 << 6) +#define UHSIC_RX_SEL (1 << 7) +#define UHSIC_RPD_DATA (1 << 9) +#define UHSIC_RPD_STROBE (1 << 10) +#define UHSIC_RPU_DATA (1 << 11) +#define UHSIC_RPU_STROBE (1 << 12) + +#define UHSIC_CMD_CFG0 0x824 +#define UHSIC_PRETEND_CONNECT_DETECT (1 << 5) + +#define UHSIC_STAT_CFG0 0x828 +#define UHSIC_CONNECT_DETECT (1 << 0) + +#define UHSIC_SPARE_CFG0 0x82c + static DEFINE_SPINLOCK(utmip_pad_lock); static int utmip_pad_count; @@ -186,6 +242,14 @@ static const u8 udc_delay_table[][4] = { {0x04, 0x66, 0x09, 0xFE}, /* 26 Mhz */ }; +static const u16 uhsic_delay_table[][4] = { + /* ENABLE_DLY, STABLE_CNT, ACTIVE_DLY, XTAL_FREQ_CNT */ + {0x02, 0x2F, 0x0, 0x1CA}, /* 12 MHz */ + {0x02, 0x33, 0x0, 0x1F0}, /* 13 MHz */ + {0x03, 0x4B, 0x0, 0x2DD}, /* 19.2 MHz */ + {0x04, 0x66, 0x0, 0x3E0}, /* 26 Mhz */ +}; + static const u16 udc_debounce_table[] = { 0x7530, /* 12 MHz */ 0x7EF4, /* 13 MHz */ @@ -214,6 +278,14 @@ static struct tegra_utmip_config utmip_default[] = { }, }; +static struct tegra_uhsic_config uhsic_default = { + .sync_start_delay = 9, + .idle_wait_delay = 17, + .term_range_adj = 0, + .elastic_underrun_limit = 16, + .elastic_overrun_limit = 16, +}; + static int utmip_pad_open(struct tegra_usb_phy *phy) { phy->pad_clk = clk_get_sys("utmip-pad", NULL); @@ -471,15 +543,15 @@ static void utmi_phy_power_on(struct tegra_usb_phy *phy) val &= ~USB_SUSP_SET; writel(val, base + USB_SUSP_CTRL); if (phy->mode == TEGRA_USB_PHY_MODE_HOST) { - gpio_status = gpio_request(TEGRA_GPIO_PD0,"VBUS_BUS"); + gpio_status = gpio_request(TEGRA_GPIO_PD0, "VBUS_BUS"); if (gpio_status < 0) { - printk("VBUS_USB1 request GPIO FAILED\n"); + printk(KERN_ERR "VBUS_USB1 request GPIO FAILED\n"); WARN_ON(1); } tegra_gpio_enable(TEGRA_GPIO_PD0); gpio_status = gpio_direction_output(TEGRA_GPIO_PD0, 1); if (gpio_status < 0) { - printk("VBUS_USB1 request GPIO DIRECTION FAILED \n"); + printk(KERN_ERR "VBUS_USB1 request GPIO DIRECTION FAILED\n"); WARN_ON(1); } gpio_set_value(TEGRA_GPIO_PD0, 1); @@ -727,7 +799,7 @@ static void null_phy_power_on(struct tegra_usb_phy *phy) /* enable null phy mode */ val = ULPIS2S_ENA; val |= ULPIS2S_PLLU_MASTER_BLASTER60; - val |= ULPIS2S_SPARE((phy->mode == TEGRA_USB_PHY_MODE_HOST)? 3:1); + val |= ULPIS2S_SPARE((phy->mode == TEGRA_USB_PHY_MODE_HOST) ? 3 : 1); writel(val, base + ULPIS2S_CTRL); /* select ULPI_CORE_CLK_SEL to SHADOW_CLK */ @@ -785,6 +857,86 @@ static void null_phy_power_off(struct tegra_usb_phy *phy) writel(val, base + ULPI_TIMING_CTRL_0); } +static void uhsic_phy_power_on(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + struct tegra_uhsic_config *config = &uhsic_default; + struct tegra_ulpi_config *ulpi_config = phy->config; + + if (ulpi_config->preinit) + ulpi_config->preinit(); + + val = readl(base + UHSIC_PADS_CFG1); + val &= ~(UHSIC_PD_BG | UHSIC_PD_TX | UHSIC_PD_TRK | UHSIC_PD_RX | + UHSIC_PD_ZI | UHSIC_RPD_DATA | UHSIC_RPD_STROBE); + val |= UHSIC_RX_SEL; + writel(val, base + UHSIC_PADS_CFG1); + udelay(2); + + val = readl(base + USB_SUSP_CTRL); + val |= UHSIC_RESET; + writel(val, base + USB_SUSP_CTRL); + udelay(30); + + val = readl(base + USB_SUSP_CTRL); + val |= UHSIC_PHY_ENABLE; + writel(val, base + USB_SUSP_CTRL); + + val = readl(base + UHSIC_HSRX_CFG0); + val |= UHSIC_IDLE_WAIT(config->idle_wait_delay); + val |= UHSIC_ELASTIC_UNDERRUN_LIMIT(config->elastic_underrun_limit); + val |= UHSIC_ELASTIC_OVERRUN_LIMIT(config->elastic_overrun_limit); + writel(val, base + UHSIC_HSRX_CFG0); + + val = readl(base + UHSIC_HSRX_CFG1); + val |= UHSIC_HS_SYNC_START_DLY(config->sync_start_delay); + writel(val, base + UHSIC_HSRX_CFG1); + + val = readl(base + UHSIC_MISC_CFG0); + val |= UHSIC_SUSPEND_EXIT_ON_EDGE; + writel(val, base + UHSIC_MISC_CFG0); + + val = readl(base + UHSIC_MISC_CFG1); + val |= UHSIC_PLLU_STABLE_COUNT(uhsic_delay_table[phy->freq_sel][1]); + writel(val, base + UHSIC_MISC_CFG1); + + val = readl(base + UHSIC_PLL_CFG1); + val |= UHSIC_PLLU_ENABLE_DLY_COUNT(uhsic_delay_table[phy->freq_sel][0]); + val |= UHSIC_XTAL_FREQ_COUNT(uhsic_delay_table[phy->freq_sel][3]); + writel(val, base + UHSIC_PLL_CFG1); + + val = readl(base + USB_SUSP_CTRL); + val &= ~(UHSIC_RESET); + writel(val, base + USB_SUSP_CTRL); + udelay(2); + + val = readl(base + USB_PORTSC1); + val &= ~USB_PORTSC1_PTS(~0); + writel(val, base + USB_PORTSC1); + + val = readl(base + USB_PORTSC1); + val &= ~(USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN); + writel(val, base + USB_PORTSC1); + + val = readl(base + UHSIC_PADS_CFG0); + val &= ~(UHSIC_TX_RTUNEN); + /* set Rtune impedance to 40 ohm */ + val |= UHSIC_TX_RTUNE(0); + writel(val, base + UHSIC_PADS_CFG0); + + if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, + USB_PHY_CLK_VALID)) { + pr_err("%s: timeout waiting for phy to stabilize\n", __func__); + } +} + +static void uhsic_phy_power_off(struct tegra_usb_phy *phy) +{ + + /* Do not do any thing here */ +} + struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode) { @@ -805,7 +957,7 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, if (!phy->config) { if (instance == 1) { - pr_err("%s: ulpi phy configuration missing", __func__); + pr_err("%s: ulpi/uhsic phy configuration missing", __func__); err = -EINVAL; goto err0; } else { @@ -867,14 +1019,15 @@ int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) { if (phy->instance == 1) { struct tegra_ulpi_config *ulpi_config = phy->config; - if (ulpi_config->inf_type == TEGRA_USB_LINK_ULPI) ulpi_phy_power_on(phy); - else + else if (ulpi_config->inf_type == TEGRA_USB_NULL_ULPI) null_phy_power_on(phy); - } else + else if (ulpi_config->inf_type == TEGRA_USB_UHSIC) + uhsic_phy_power_on(phy); + } else { utmi_phy_power_on(phy); - + } return 0; } @@ -882,14 +1035,15 @@ int tegra_usb_phy_power_off(struct tegra_usb_phy *phy) { if (phy->instance == 1) { struct tegra_ulpi_config *ulpi_config = phy->config; - if (ulpi_config->inf_type == TEGRA_USB_LINK_ULPI) ulpi_phy_power_off(phy); - else + else if (ulpi_config->inf_type == TEGRA_USB_NULL_ULPI) null_phy_power_off(phy); - } else + else if (ulpi_config->inf_type == TEGRA_USB_UHSIC) + uhsic_phy_power_off(phy); + } else { utmi_phy_power_off(phy); - + } return 0; } @@ -952,3 +1106,158 @@ int tegra_usb_phy_close(struct tegra_usb_phy *phy) kfree(phy); return 0; } + +int tegra_usb_phy_bus_connect(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + struct tegra_ulpi_config *config = phy->config; + + if ((phy->instance == 1) && + (config->inf_type == TEGRA_USB_UHSIC)) { + + val = readl(base + UHSIC_MISC_CFG0); + val |= UHSIC_DETECT_SHORT_CONNECT; + writel(val, base + UHSIC_MISC_CFG0); + udelay(1); + + val = readl(base + UHSIC_MISC_CFG0); + val |= UHSIC_FORCE_XCVR_MODE; + writel(val, base + UHSIC_MISC_CFG0); + + val = readl(base + UHSIC_PADS_CFG1); + val &= ~UHSIC_RPD_STROBE; + val |= UHSIC_RPU_STROBE; + writel(val, base + UHSIC_PADS_CFG1); + + if (utmi_wait_register(base + UHSIC_STAT_CFG0, UHSIC_CONNECT_DETECT, UHSIC_CONNECT_DETECT) < 0) { + pr_err("%s: timeout waiting for hsic connect detect\n", __func__); + return -ETIMEDOUT; + } + + if (utmi_wait_register(base + USB_PORTSC1, USB_PORTSC1_LS(2), USB_PORTSC1_LS(2)) < 0) { + pr_err("%s: timeout waiting for dplus state\n", __func__); + return -ETIMEDOUT; + } + } + + return 0; +} + +int tegra_usb_phy_bus_reset(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + struct tegra_ulpi_config *config = phy->config; + + if ((phy->instance == 1) && + (config->inf_type == TEGRA_USB_UHSIC)) { + + val = readl(base + USB_PORTSC1); + val |= USB_PORTSC1_PTC(5); + writel(val, base + USB_PORTSC1); + udelay(2); + + val = readl(base + USB_PORTSC1); + val &= ~USB_PORTSC1_PTC(~0); + writel(val, base + USB_PORTSC1); + udelay(2); + + if (utmi_wait_register(base + USB_PORTSC1, USB_PORTSC1_LS(0), 0) < 0) { + pr_err("%s: timeout waiting for SE0\n", __func__); + return -ETIMEDOUT; + } + + if (utmi_wait_register(base + USB_PORTSC1, USB_PORTSC1_CCS, USB_PORTSC1_CCS) < 0) { + pr_err("%s: timeout waiting for connection status\n", __func__); + return -ETIMEDOUT; + } + + if (utmi_wait_register(base + USB_PORTSC1, USB_PORTSC1_PSPD(2), USB_PORTSC1_PSPD(2)) < 0) { + pr_err("%s: timeout waiting hsic high speed configuration\n", __func__); + return -ETIMEDOUT; + } + + val = readl(base + USB_USBCMD); + val &= ~USB_USBCMD_RS; + writel(val, base + USB_USBCMD); + + if (utmi_wait_register(base + USB_USBSTS, USB_USBSTS_HCH, USB_USBSTS_HCH) < 0) { + pr_err("%s: timeout waiting for stopping the controller\n", __func__); + return -ETIMEDOUT; + } + + val = readl(base + UHSIC_PADS_CFG1); + val &= ~UHSIC_RPU_STROBE; + val |= UHSIC_RPD_STROBE; + writel(val, base + UHSIC_PADS_CFG1); + + mdelay(50); + + val = readl(base + UHSIC_PADS_CFG1); + val &= ~UHSIC_RPD_STROBE; + val |= UHSIC_RPU_STROBE; + writel(val, base + UHSIC_PADS_CFG1); + + val = readl(base + USB_USBCMD); + val |= USB_USBCMD_RS; + writel(val, base + USB_USBCMD); + + val = readl(base + UHSIC_PADS_CFG1); + val &= ~UHSIC_RPU_STROBE; + writel(val, base + UHSIC_PADS_CFG1); + + if (utmi_wait_register(base + USB_USBCMD, USB_USBCMD_RS, USB_USBCMD_RS) < 0) { + pr_err("%s: timeout waiting for starting the controller\n", __func__); + return -ETIMEDOUT; + } + } + + return 0; +} + +int tegra_usb_phy_bus_idle(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + struct tegra_ulpi_config *config = phy->config; + + if ((phy->instance == 1) && + (config->inf_type == TEGRA_USB_UHSIC)) { + + val = readl(base + UHSIC_MISC_CFG0); + val |= UHSIC_DETECT_SHORT_CONNECT; + writel(val, base + UHSIC_MISC_CFG0); + udelay(1); + + val = readl(base + UHSIC_MISC_CFG0); + val |= UHSIC_FORCE_XCVR_MODE; + writel(val, base + UHSIC_MISC_CFG0); + + val = readl(base + UHSIC_PADS_CFG1); + val &= ~UHSIC_RPD_STROBE; + val |= UHSIC_RPU_STROBE; + writel(val, base + UHSIC_PADS_CFG1); + } + return 0; +} + +bool tegra_usb_phy_is_device_connected(struct tegra_usb_phy *phy) +{ + void __iomem *base = phy->regs; + struct tegra_ulpi_config *config = phy->config; + + if ((phy->instance == 1) && + (config->inf_type == TEGRA_USB_UHSIC)) { + if (!((readl(base + UHSIC_STAT_CFG0) & UHSIC_CONNECT_DETECT) == UHSIC_CONNECT_DETECT)) { + pr_err("%s: hsic no device connection\n", __func__); + return false; + } + if (utmi_wait_register(base + USB_PORTSC1, USB_PORTSC1_LS(2), USB_PORTSC1_LS(2)) < 0) { + pr_err("%s: timeout waiting for dplus state\n", __func__); + return false; + } + } + return true; +} + |