summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorSeshendra Gadagottu <sgadagottu@nvidia.com>2011-01-11 11:41:09 -0800
committerVarun Colbert <vcolbert@nvidia.com>2011-02-10 15:41:11 -0800
commit8df80194dc456a45fc8245de57bd9b46d2cf16a8 (patch)
tree78a310256698305e2ac7dae9df58b39fd5cacec7 /arch
parent57a3407736092650cbed228fdf9264e140378762 (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.h24
-rw-r--r--arch/arm/mach-tegra/usb_phy.c341
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;
+}
+