summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/Kconfig3
-rw-r--r--arch/arm/mach-tegra/include/mach/system.h4
-rw-r--r--arch/arm/mach-tegra/include/mach/usb_phy.h26
-rw-r--r--arch/arm/mach-tegra/tegra2_emc.c85
-rw-r--r--arch/arm/mach-tegra/tegra2_emc.h13
-rw-r--r--arch/arm/mach-tegra/usb_phy.c195
-rw-r--r--drivers/serial/tegra_hsuart.c87
-rw-r--r--drivers/usb/Kconfig1
-rw-r--r--drivers/usb/host/Kconfig16
-rw-r--r--drivers/usb/host/ehci-hcd.c5
-rw-r--r--drivers/usb/host/ehci-tegra.c234
-rw-r--r--drivers/usb/host/ehci.h1
-rw-r--r--drivers/usb/otg/Kconfig7
-rw-r--r--drivers/usb/otg/Makefile1
-rw-r--r--drivers/usb/otg/tegra-otg.c4
-rw-r--r--drivers/usb/otg/ulpi_viewport.c80
-rwxr-xr-xdrivers/video/tegra/dc/dc.c3
-rw-r--r--drivers/video/tegra/dc/hdmi.c6
-rw-r--r--include/linux/platform_data/tegra_usb.h36
-rw-r--r--include/linux/usb/ehci_def.h4
-rw-r--r--include/linux/usb/ulpi.h5
21 files changed, 495 insertions, 321 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index 2bbd98bda7cc..9ad8c961c571 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -11,6 +11,9 @@ config ARCH_TEGRA_2x_SOC
select ARM_GIC
select ARCH_REQUIRE_GPIOLIB
select ARM_ERRATA_742230
+ select USB_ARCH_HAS_EHCI if USB_SUPPORT
+ select USB_ULPI if USB_SUPPORT
+ select USB_ULPI_VIEWPORT if USB_SUPPORT
help
Support for NVIDIA Tegra AP20 and T20 processors, based on the
ARM CortexA9MP CPU and the ARM PL310 L2 cache controller
diff --git a/arch/arm/mach-tegra/include/mach/system.h b/arch/arm/mach-tegra/include/mach/system.h
index d7e807bb564f..9cd6ddf519d1 100644
--- a/arch/arm/mach-tegra/include/mach/system.h
+++ b/arch/arm/mach-tegra/include/mach/system.h
@@ -35,9 +35,9 @@ static inline void tegra_assert_system_reset(void)
void __iomem *reset = IO_ADDRESS(TEGRA_PMC_BASE + 0x00);
u32 reg;
- reg = readl(reset);
+ reg = readl_relaxed(reset);
reg |= 0x10;
- writel(reg, reset);
+ writel_relaxed(reg, reset);
}
static inline void arch_reset(char mode, const char *cmd)
diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h
index 07976ef24865..0dccea650737 100644
--- a/arch/arm/mach-tegra/include/mach/usb_phy.h
+++ b/arch/arm/mach-tegra/include/mach/usb_phy.h
@@ -18,11 +18,9 @@
#ifndef __MACH_USB_PHY_H
#define __MACH_USB_PHY_H
-#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
-
-#define USB_PHY_MAX_CONTEXT_REGS 10
+#include <linux/usb/otg.h>
struct tegra_utmip_config {
u8 hssync_start_delay;
@@ -67,7 +65,7 @@ struct tegra_uhsic_config {
enum tegra_usb_phy_port_speed {
TEGRA_USB_PHY_PORT_SPEED_FULL = 0,
TEGRA_USB_PHY_PORT_SPEED_LOW,
- TEGRA_USB_PHY_PORT_HIGH,
+ TEGRA_USB_PHY_PORT_SPEED_HIGH,
};
enum tegra_usb_phy_mode {
@@ -81,9 +79,12 @@ struct usb_phy_plat_data {
int vbus_gpio;
};
+struct tegra_xtal_freq;
+
struct tegra_usb_phy {
int instance;
int freq_sel;
+ const struct tegra_xtal_freq *freq;
void __iomem *regs;
void __iomem *pad_regs;
struct clk *clk;
@@ -93,6 +94,7 @@ struct tegra_usb_phy {
void *config;
struct regulator *reg_vdd;
bool regulator_on;
+ struct otg_transceiver *ulpi;
};
struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
@@ -100,22 +102,22 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
int tegra_usb_phy_power_on(struct tegra_usb_phy *phy);
-int tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy);
+void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy);
-int tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy);
+void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy);
-int tegra_usb_phy_power_off(struct tegra_usb_phy *phy);
+void tegra_usb_phy_power_off(struct tegra_usb_phy *phy);
-int tegra_usb_phy_preresume(struct tegra_usb_phy *phy);
+void tegra_usb_phy_preresume(struct tegra_usb_phy *phy);
-int tegra_usb_phy_postresume(struct tegra_usb_phy *phy);
+void tegra_usb_phy_postresume(struct tegra_usb_phy *phy);
-int tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy,
+void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy,
enum tegra_usb_phy_port_speed port_speed);
-int tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy);
+void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy);
-int tegra_usb_phy_close(struct tegra_usb_phy *phy);
+void tegra_usb_phy_close(struct tegra_usb_phy *phy);
int tegra_usb_phy_bus_connect(struct tegra_usb_phy *phy);
diff --git a/arch/arm/mach-tegra/tegra2_emc.c b/arch/arm/mach-tegra/tegra2_emc.c
index bd4fa27b2086..47ba71672b86 100644
--- a/arch/arm/mach-tegra/tegra2_emc.c
+++ b/arch/arm/mach-tegra/tegra2_emc.c
@@ -25,6 +25,11 @@
#include "tegra2_emc.h"
+#define TEGRA_MRR_DIVLD (1<<20)
+#define TEGRA_EMC_STATUS 0x02b4
+#define TEGRA_EMC_MRR 0x00ec
+static DEFINE_MUTEX(tegra_emc_mrr_lock);
+
#ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE
static bool emc_enable = true;
#else
@@ -46,6 +51,35 @@ static inline u32 emc_readl(unsigned long addr)
return readl(emc + addr);
}
+/* read LPDDR2 memory modes */
+static int tegra_emc_read_mrr(unsigned long addr)
+{
+ u32 value;
+ int count = 100;
+
+ mutex_lock(&tegra_emc_mrr_lock);
+ do {
+ emc_readl(TEGRA_EMC_MRR);
+ } while (--count && (emc_readl(TEGRA_EMC_STATUS) & TEGRA_MRR_DIVLD));
+ if (count == 0) {
+ pr_err("%s: Failed to read memory type\n", __func__);
+ BUG();
+ }
+ value = (1 << 30) | (addr << 16);
+ emc_writel(value, TEGRA_EMC_MRR);
+
+ count = 100;
+ while (--count && !(emc_readl(TEGRA_EMC_STATUS) & TEGRA_MRR_DIVLD));
+ if (count == 0) {
+ pr_err("%s: Failed to read memory type\n", __func__);
+ BUG();
+ }
+ value = emc_readl(TEGRA_EMC_MRR) & 0xFFFF;
+ mutex_unlock(&tegra_emc_mrr_lock);
+
+ return value;
+}
+
static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = {
0x2c, /* RC */
0x30, /* RFC */
@@ -165,8 +199,53 @@ int tegra_emc_set_rate(unsigned long rate)
return 0;
}
-void tegra_init_emc(const struct tegra_emc_table *table, int table_size)
+void tegra_init_emc(const struct tegra_emc_chip *chips, int chips_size)
{
- tegra_emc_table = table;
- tegra_emc_table_size = table_size;
+ int i;
+ int vid;
+ int rev_id1;
+ int rev_id2;
+ int pid;
+ int chip_matched = -1;
+
+ vid = tegra_emc_read_mrr(5);
+ rev_id1 = tegra_emc_read_mrr(6);
+ rev_id2 = tegra_emc_read_mrr(7);
+ pid = tegra_emc_read_mrr(8);
+
+ for (i = 0; i < chips_size; i++) {
+ if (chips[i].mem_manufacturer_id >= 0) {
+ if (chips[i].mem_manufacturer_id != vid)
+ continue;
+ }
+ if (chips[i].mem_revision_id1 >= 0) {
+ if (chips[i].mem_revision_id1 != rev_id1)
+ continue;
+ }
+ if (chips[i].mem_revision_id2 >= 0) {
+ if (chips[i].mem_revision_id2 != rev_id2)
+ continue;
+ }
+ if (chips[i].mem_pid >= 0) {
+ if (chips[i].mem_pid != pid)
+ continue;
+ }
+
+ chip_matched = i;
+ break;
+ }
+
+ if (chip_matched >= 0) {
+ pr_info("%s: %s memory found\n", __func__,
+ chips[chip_matched].description);
+ tegra_emc_table = chips[chip_matched].table;
+ tegra_emc_table_size = chips[chip_matched].table_size;
+ } else {
+ pr_err("%s: Memory not recognized, memory scaling disabled\n",
+ __func__);
+ pr_info("%s: Memory vid = 0x%04x", __func__, vid);
+ pr_info("%s: Memory rev_id1 = 0x%04x", __func__, rev_id1);
+ pr_info("%s: Memory rev_id2 = 0x%04x", __func__, rev_id2);
+ pr_info("%s: Memory pid = 0x%04x", __func__, pid);
+ }
}
diff --git a/arch/arm/mach-tegra/tegra2_emc.h b/arch/arm/mach-tegra/tegra2_emc.h
index 3515e57fd0d9..4f060f7355d6 100644
--- a/arch/arm/mach-tegra/tegra2_emc.h
+++ b/arch/arm/mach-tegra/tegra2_emc.h
@@ -22,6 +22,17 @@ struct tegra_emc_table {
u32 regs[TEGRA_EMC_NUM_REGS];
};
+struct tegra_emc_chip {
+ const char *description;
+ int mem_manufacturer_id; /* LPDDR2 MR5 or -1 to ignore */
+ int mem_revision_id1; /* LPDDR2 MR6 or -1 to ignore */
+ int mem_revision_id2; /* LPDDR2 MR7 or -1 to ignore */
+ int mem_pid; /* LPDDR2 MR8 or -1 to ignore */
+
+ const struct tegra_emc_table *table;
+ int table_size;
+};
+
int tegra_emc_set_rate(unsigned long rate);
long tegra_emc_round_rate(unsigned long rate);
-void tegra_init_emc(const struct tegra_emc_table *table, int table_size);
+void tegra_init_emc(const struct tegra_emc_chip *chips, int chips_size);
diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c
index 0ed797a75c47..968c443cf099 100644
--- a/arch/arm/mach-tegra/usb_phy.c
+++ b/arch/arm/mach-tegra/usb_phy.c
@@ -27,6 +27,8 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/gpio.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/ulpi.h>
#include <asm/mach-types.h>
#include <mach/usb_phy.h>
#include <mach/iomap.h>
@@ -41,14 +43,6 @@
#define USB_USBSTS_HCH (1 << 12)
#define ULPI_VIEWPORT 0x170
-#define ULPI_WAKEUP (1 << 31)
-#define ULPI_RUN (1 << 30)
-#define ULPI_RD_RW_WRITE (1 << 29)
-#define ULPI_RD_RW_READ (0 << 29)
-#define ULPI_PORT(x) (((x) & 0x7) << 24)
-#define ULPI_ADDR(x) (((x) & 0xff) << 16)
-#define ULPI_DATA_RD(x) (((x) & 0xff) << 8)
-#define ULPI_DATA_WR(x) (((x) & 0xff) << 0)
#define USB_PORTSC1 0x184
#define USB_PORTSC1_PTS(x) (((x) & 0x3) << 30)
@@ -228,19 +222,13 @@
static DEFINE_SPINLOCK(utmip_pad_lock);
static int utmip_pad_count;
-static const int udc_freq_table[] = {
- 12000000,
- 13000000,
- 19200000,
- 26000000,
-};
-
-static const u8 udc_delay_table[][4] = {
- /* ENABLE_DLY, STABLE_CNT, ACTIVE_DLY, XTAL_FREQ_CNT */
- {0x02, 0x2F, 0x04, 0x76}, /* 12 MHz */
- {0x02, 0x33, 0x05, 0x7F}, /* 13 MHz */
- {0x03, 0x4B, 0x06, 0xBB}, /* 19.2 MHz */
- {0x04, 0x66, 0x09, 0xFE}, /* 26 Mhz */
+struct tegra_xtal_freq {
+ int freq;
+ u8 enable_delay;
+ u8 stable_count;
+ u8 active_delay;
+ u8 xtal_freq_count;
+ u16 debounce;
};
static const u16 uhsic_delay_table[][4] = {
@@ -251,11 +239,39 @@ static const u16 uhsic_delay_table[][4] = {
{0x04, 0x66, 0x0, 0x3E0}, /* 26 Mhz */
};
-static const u16 udc_debounce_table[] = {
- 0x7530, /* 12 MHz */
- 0x7EF4, /* 13 MHz */
- 0xBB80, /* 19.2 MHz */
- 0xFDE8, /* 26 MHz */
+static const struct tegra_xtal_freq tegra_freq_table[] = {
+ {
+ .freq = 12000000,
+ .enable_delay = 0x02,
+ .stable_count = 0x2F,
+ .active_delay = 0x04,
+ .xtal_freq_count = 0x76,
+ .debounce = 0x7530,
+ },
+ {
+ .freq = 13000000,
+ .enable_delay = 0x02,
+ .stable_count = 0x33,
+ .active_delay = 0x05,
+ .xtal_freq_count = 0x7F,
+ .debounce = 0x7EF4,
+ },
+ {
+ .freq = 19200000,
+ .enable_delay = 0x03,
+ .stable_count = 0x4B,
+ .active_delay = 0x06,
+ .xtal_freq_count = 0xBB,
+ .debounce = 0xBB80,
+ },
+ {
+ .freq = 26000000,
+ .enable_delay = 0x04,
+ .stable_count = 0x66,
+ .active_delay = 0x09,
+ .xtal_freq_count = 0xFE,
+ .debounce = 0xFDE8,
+ },
};
static struct tegra_utmip_config utmip_default[] = {
@@ -293,12 +309,17 @@ struct usb_phy_plat_data usb_phy_data[] = {
{ 0, 0, -1},
};
+static inline bool phy_is_ulpi(struct tegra_usb_phy *phy)
+{
+ return (phy->instance == 1);
+}
+
static int utmip_pad_open(struct tegra_usb_phy *phy)
{
phy->pad_clk = clk_get_sys("utmip-pad", NULL);
if (IS_ERR(phy->pad_clk)) {
pr_err("%s: can't get utmip pad clock\n", __func__);
- return -1;
+ return PTR_ERR(phy->pad_clk);
}
if (phy->instance == 0) {
@@ -348,7 +369,7 @@ static int utmip_pad_power_off(struct tegra_usb_phy *phy)
if (!utmip_pad_count) {
pr_err("%s: utmip pad already powered off\n", __func__);
- return -1;
+ return -EINVAL;
}
clk_enable(phy->pad_clk);
@@ -467,7 +488,7 @@ static void vbus_disable(int gpio)
gpio_free(gpio);
}
-static void utmi_phy_power_on(struct tegra_usb_phy *phy)
+static int utmi_phy_power_on(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
@@ -500,7 +521,7 @@ static void utmi_phy_power_on(struct tegra_usb_phy *phy)
val = readl(base + UTMIP_DEBOUNCE_CFG0);
val &= ~UTMIP_BIAS_DEBOUNCE_A(~0);
- val |= UTMIP_BIAS_DEBOUNCE_A(udc_debounce_table[phy->freq_sel]);
+ val |= UTMIP_BIAS_DEBOUNCE_A(phy->freq->debounce);
writel(val, base + UTMIP_DEBOUNCE_CFG0);
val = readl(base + UTMIP_MISC_CFG0);
@@ -509,14 +530,14 @@ static void utmi_phy_power_on(struct tegra_usb_phy *phy)
val = readl(base + UTMIP_MISC_CFG1);
val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) | UTMIP_PLLU_STABLE_COUNT(~0));
- val |= UTMIP_PLL_ACTIVE_DLY_COUNT(udc_delay_table[phy->freq_sel][2]) |
- UTMIP_PLLU_STABLE_COUNT(udc_delay_table[phy->freq_sel][1]);
+ val |= UTMIP_PLL_ACTIVE_DLY_COUNT(phy->freq->active_delay) |
+ UTMIP_PLLU_STABLE_COUNT(phy->freq->stable_count);
writel(val, base + UTMIP_MISC_CFG1);
val = readl(base + UTMIP_PLL_CFG1);
val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) | UTMIP_PLLU_ENABLE_DLY_COUNT(~0));
- val |= UTMIP_XTAL_FREQ_COUNT(udc_delay_table[phy->freq_sel][3]) |
- UTMIP_PLLU_ENABLE_DLY_COUNT(udc_delay_table[phy->freq_sel][0]);
+ val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) |
+ UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
writel(val, base + UTMIP_PLL_CFG1);
if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) {
@@ -592,6 +613,8 @@ static void utmi_phy_power_on(struct tegra_usb_phy *phy)
if (phy->mode == TEGRA_USB_PHY_MODE_HOST) {
vbus_enable(usb_phy_data[phy->instance].vbus_gpio);
}
+
+ return 0;
}
static void utmi_phy_power_off(struct tegra_usb_phy *phy)
@@ -685,21 +708,9 @@ static void utmi_phy_restore_end(struct tegra_usb_phy *phy)
udelay(10);
}
-static void ulpi_viewport_write(struct tegra_usb_phy *phy, u8 addr, u8 data)
-{
- unsigned long val;
- void __iomem *base = phy->regs;
-
- val = ULPI_RUN | ULPI_RD_RW_WRITE | ULPI_PORT(0);
- val |= ULPI_ADDR(addr) | ULPI_DATA_WR(data);
- writel(val, base + ULPI_VIEWPORT);
-
- if (utmi_wait_register(base + ULPI_VIEWPORT, ULPI_RUN, 0))
- pr_err("%s: timeout accessing ulpi phy\n", __func__);
-}
-
-static void ulpi_phy_power_on(struct tegra_usb_phy *phy)
+static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
{
+ int ret;
unsigned long val;
void __iomem *base = phy->regs;
struct tegra_ulpi_config *config = phy->config;
@@ -737,17 +748,18 @@ static void ulpi_phy_power_on(struct tegra_usb_phy *phy)
val |= ULPI_DIR_TRIMMER_LOAD;
writel(val, base + ULPI_TIMING_CTRL_1);
- val = ULPI_WAKEUP | ULPI_RD_RW_WRITE | ULPI_PORT(0);
- writel(val, base + ULPI_VIEWPORT);
-
- if (utmi_wait_register(base + ULPI_VIEWPORT, ULPI_WAKEUP, 0)) {
- pr_err("%s: timeout waiting for ulpi phy wakeup\n", __func__);
- return;
+ /* Fix VbusInvalid due to floating VBUS */
+ ret = otg_io_write(phy->ulpi, 0x40, 0x08);
+ if (ret) {
+ pr_err("%s: ulpi write failed\n", __func__);
+ return ret;
}
- /* Fix VbusInvalid due to floating VBUS */
- ulpi_viewport_write(phy, 0x08, 0x40);
- ulpi_viewport_write(phy, 0x0B, 0x80);
+ ret = otg_io_write(phy->ulpi, 0x80, 0x0B);
+ if (ret) {
+ pr_err("%s: ulpi write failed\n", __func__);
+ return ret;
+ }
val = readl(base + USB_PORTSC1);
val |= USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN;
@@ -761,6 +773,8 @@ static void ulpi_phy_power_on(struct tegra_usb_phy *phy)
val = readl(base + USB_SUSP_CTRL);
val &= ~USB_SUSP_CLR;
writel(val, base + USB_SUSP_CTRL);
+
+ return 0;
}
static void ulpi_phy_power_off(struct tegra_usb_phy *phy)
@@ -995,7 +1009,7 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
struct tegra_usb_phy *phy;
struct tegra_ulpi_config *ulpi_config;
unsigned long parent_rate;
- int freq_sel;
+ int i;
int err;
phy = kmalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL);
@@ -1009,8 +1023,8 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
phy->regulator_on = 0;
if (!phy->config) {
- if (instance == 1) {
- pr_err("%s: ulpi/uhsic phy configuration missing", __func__);
+ if (phy_is_ulpi(phy)) {
+ pr_err("%s: ulpi phy configuration missing", __func__);
err = -EINVAL;
goto err0;
} else {
@@ -1027,18 +1041,20 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
clk_enable(phy->pll_u);
parent_rate = clk_get_rate(clk_get_parent(phy->pll_u));
- for (freq_sel = 0; freq_sel < ARRAY_SIZE(udc_freq_table); freq_sel++) {
- if (udc_freq_table[freq_sel] == parent_rate)
+ for (i = 0; i < ARRAY_SIZE(tegra_freq_table); i++) {
+ if (tegra_freq_table[i].freq == parent_rate) {
+ phy->freq = &tegra_freq_table[i];
break;
+ }
}
- if (freq_sel == ARRAY_SIZE(udc_freq_table)) {
+ if (!phy->freq) {
pr_err("invalid pll_u parent rate %ld\n", parent_rate);
err = -EINVAL;
goto err1;
}
- phy->freq_sel = freq_sel;
+ phy->freq_sel = i;
- if (phy->instance == 1) {
+ if (phy_is_ulpi(phy)) {
ulpi_config = config;
if (ulpi_config->inf_type == TEGRA_USB_LINK_ULPI) {
@@ -1051,6 +1067,9 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
tegra_gpio_enable(ulpi_config->reset_gpio);
gpio_request(ulpi_config->reset_gpio, "ulpi_phy_reset_b");
gpio_direction_output(ulpi_config->reset_gpio, 0);
+
+ phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0);
+ phy->ulpi->io_priv = regs + ULPI_VIEWPORT;
}
} else {
err = utmip_pad_open(phy);
@@ -1090,7 +1109,7 @@ int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
regulator_enable(phy->reg_vdd);
phy->regulator_on = 1;
}
- if (phy->instance == 1) {
+ if (phy_is_ulpi(phy)) {
struct tegra_ulpi_config *ulpi_config = phy->config;
if (ulpi_config->inf_type == TEGRA_USB_LINK_ULPI)
ulpi_phy_power_on(phy);
@@ -1104,9 +1123,9 @@ int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
return 0;
}
-int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
+void tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
{
- if (phy->instance == 1) {
+ if (phy_is_ulpi(phy)) {
struct tegra_ulpi_config *ulpi_config = phy->config;
if (ulpi_config->inf_type == TEGRA_USB_LINK_ULPI)
ulpi_phy_power_off(phy);
@@ -1122,57 +1141,48 @@ int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
regulator_disable(phy->reg_vdd);
phy->regulator_on = 0;
}
- return 0;
}
-int tegra_usb_phy_preresume(struct tegra_usb_phy *phy)
+void tegra_usb_phy_preresume(struct tegra_usb_phy *phy)
{
- if (phy->instance != 1)
+ if (!phy_is_ulpi(phy))
utmi_phy_preresume(phy);
- return 0;
}
-int tegra_usb_phy_postresume(struct tegra_usb_phy *phy)
+void tegra_usb_phy_postresume(struct tegra_usb_phy *phy)
{
- if (phy->instance != 1)
+ if (!phy_is_ulpi(phy))
utmi_phy_postresume(phy);
- return 0;
}
-int tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy,
+void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy,
enum tegra_usb_phy_port_speed port_speed)
{
- if (phy->instance != 1)
+ if (!phy_is_ulpi(phy))
utmi_phy_restore_start(phy, port_speed);
- return 0;
}
-int tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy)
+void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy)
{
- if (phy->instance != 1)
+ if (!phy_is_ulpi(phy))
utmi_phy_restore_end(phy);
- return 0;
}
-int tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy)
+void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy)
{
- if (phy->instance != 1)
+ if (!phy_is_ulpi(phy))
utmi_phy_clk_disable(phy);
-
- return 0;
}
-int tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy)
+void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy)
{
- if (phy->instance != 1)
+ if (!phy_is_ulpi(phy))
utmi_phy_clk_enable(phy);
-
- return 0;
}
-int tegra_usb_phy_close(struct tegra_usb_phy *phy)
+void tegra_usb_phy_close(struct tegra_usb_phy *phy)
{
- if (phy->instance == 1) {
+ if (phy_is_ulpi(phy)) {
struct tegra_ulpi_config *ulpi_config = phy->config;
if (ulpi_config->inf_type == TEGRA_USB_LINK_ULPI)
@@ -1185,7 +1195,6 @@ int tegra_usb_phy_close(struct tegra_usb_phy *phy)
if (phy->instance == 0 && usb_phy_data[0].vbus_irq)
free_irq(usb_phy_data[0].vbus_irq, phy);
kfree(phy);
- return 0;
}
int tegra_usb_phy_bus_connect(struct tegra_usb_phy *phy)
@@ -1353,4 +1362,6 @@ int __init tegra_usb_phy_init(struct usb_phy_plat_data *pdata, int size)
usb_phy_data[pdata->instance].vbus_gpio = pdata->vbus_gpio;
}
}
+
+ return 0;
}
diff --git a/drivers/serial/tegra_hsuart.c b/drivers/serial/tegra_hsuart.c
index b683a3d5d2cd..6c669cb613ec 100644
--- a/drivers/serial/tegra_hsuart.c
+++ b/drivers/serial/tegra_hsuart.c
@@ -324,6 +324,51 @@ static void do_handle_rx_dma(struct tegra_uart_port *t)
set_rts(t, true);
}
+/* Wait for a symbol-time. */
+static void wait_sym_time(struct tegra_uart_port *t, unsigned int syms)
+{
+
+ /* Definitely have a start bit. */
+ unsigned int bits = 1;
+ switch (t->lcr_shadow & 3) {
+ case UART_LCR_WLEN5:
+ bits += 5;
+ break;
+ case UART_LCR_WLEN6:
+ bits += 6;
+ break;
+ case UART_LCR_WLEN7:
+ bits += 7;
+ break;
+ default:
+ bits += 8;
+ break;
+ }
+
+ /* Technically 5 bits gets 1.5 bits of stop... */
+ if (t->lcr_shadow & UART_LCR_STOP) {
+ bits += 2;
+ } else {
+ bits++;
+ }
+
+ if (t->lcr_shadow & UART_LCR_PARITY)
+ bits++;
+
+ if (likely(t->baud))
+ udelay(DIV_ROUND_UP(syms * bits * 1000000, t->baud));
+}
+
+/* Flush desired FIFO. */
+static void tegra_fifo_reset(struct tegra_uart_port *t, u8 fcr_bits)
+{
+ unsigned char fcr = t->fcr_shadow;
+ fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ uart_writeb(t, fcr, UART_FCR);
+ uart_readb(t, UART_SCR); /* Dummy read to ensure the write is posted */
+ wait_sym_time(t, 1); /* Wait for the flush to propagate. */
+}
+
static char do_decode_rx_error(struct tegra_uart_port *t, u8 lsr)
{
char flag = TTY_NORMAL;
@@ -347,11 +392,8 @@ static char do_decode_rx_error(struct tegra_uart_port *t, u8 lsr)
dev_err(t->uport.dev, "Got Break\n");
t->uport.icount.brk++;
/* If FIFO read error without any data, reset Rx FIFO */
- if (!(lsr & UART_LSR_DR) && (lsr & UART_LSR_FIFOE)) {
- unsigned char fcr = t->fcr_shadow;
- fcr |= UART_FCR_CLEAR_RCVR;
- uart_writeb(t, fcr, UART_FCR);
- }
+ if (!(lsr & UART_LSR_DR) && (lsr & UART_LSR_FIFOE))
+ tegra_fifo_reset(t, UART_FCR_CLEAR_RCVR);
}
}
return flag;
@@ -538,7 +580,6 @@ static void tegra_stop_rx(struct uart_port *u)
static void tegra_uart_hw_deinit(struct tegra_uart_port *t)
{
- unsigned char fcr;
unsigned long flags;
/* Disable interrupts */
@@ -550,11 +591,7 @@ static void tegra_uart_hw_deinit(struct tegra_uart_port *t)
spin_lock_irqsave(&t->uport.lock, flags);
/* Reset the Rx and Tx FIFOs */
- fcr = t->fcr_shadow;
- fcr |= UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR;
- uart_writeb(t, fcr, UART_FCR);
-
- udelay(200);
+ tegra_fifo_reset(t, UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR);
clk_disable(t->clk);
t->baud = 0;
@@ -582,7 +619,6 @@ static void tegra_uart_free_rx_dma(struct tegra_uart_port *t)
static int tegra_uart_hw_init(struct tegra_uart_port *t)
{
- unsigned char fcr;
unsigned char ier;
dev_vdbg(t->uport.dev, "+tegra_uart_hw_init\n");
@@ -603,20 +639,6 @@ static int tegra_uart_hw_init(struct tegra_uart_port *t)
t->rx_in_progress = 0;
- /* Reset the FIFO twice with some delay to make sure that the FIFOs are
- * really flushed. Wait is needed as the clearing needs to cross
- * multiple clock domains.
- * */
- t->fcr_shadow = UART_FCR_ENABLE_FIFO;
-
- fcr = t->fcr_shadow;
- fcr |= UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR;
- uart_writeb(t, fcr, UART_FCR);
-
- udelay(100);
- uart_writeb(t, t->fcr_shadow, UART_FCR);
- udelay(100);
-
/* Set the trigger level
*
* For PIO mode:
@@ -638,6 +660,7 @@ static int tegra_uart_hw_init(struct tegra_uart_port *t)
* Set the Tx trigger to 4. This should match the DMA burst size that
* programmed in the DMA registers.
* */
+ t->fcr_shadow = UART_FCR_ENABLE_FIFO;
t->fcr_shadow |= UART_FCR_R_TRIG_01;
t->fcr_shadow |= TEGRA_UART_TX_TRIG_8B;
uart_writeb(t, t->fcr_shadow, UART_FCR);
@@ -646,7 +669,7 @@ static int tegra_uart_hw_init(struct tegra_uart_port *t)
/* initialize the UART for a simple default configuration
* so that the receive DMA buffer may be enqueued */
t->lcr_shadow = 3; /* no parity, stop, 8 data bits */
- tegra_set_baudrate(t, 9600);
+ tegra_set_baudrate(t, 115200);
t->fcr_shadow |= UART_FCR_DMA_SELECT;
uart_writeb(t, t->fcr_shadow, UART_FCR);
if (tegra_start_dma_rx(t)) {
@@ -987,8 +1010,10 @@ static void tegra_set_baudrate(struct tegra_uart_port *t, unsigned int baud)
lcr &= ~UART_LCR_DLAB;
uart_writeb(t, lcr, UART_LCR);
+ uart_readb(t, UART_SCR); /* Dummy read to ensure the write is posted */
t->baud = baud;
+ wait_sym_time(t, 2); /* wait two character intervals at new rate */
dev_dbg(t->uport.dev, "Baud %u clock freq %lu and divisor of %u\n",
baud, rate, divisor);
}
@@ -1012,10 +1037,6 @@ static void tegra_set_termios(struct uart_port *u, struct ktermios *termios,
if (t->rts_active)
set_rts(t, false);
- /* Baud rate */
- baud = uart_get_baud_rate(u, termios, oldtermios, 200, 4000000);
- tegra_set_baudrate(t, baud);
-
/* Parity */
lcr = t->lcr_shadow;
lcr &= ~UART_LCR_PARITY;
@@ -1059,6 +1080,10 @@ static void tegra_set_termios(struct uart_port *u, struct ktermios *termios,
uart_writeb(t, lcr, UART_LCR);
t->lcr_shadow = lcr;
+ /* Baud rate. */
+ baud = uart_get_baud_rate(u, termios, oldtermios, 200, 4000000);
+ tegra_set_baudrate(t, baud);
+
/* Flow control */
if (termios->c_cflag & CRTSCTS) {
mcr = t->mcr_shadow;
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 69e8a096c35a..4aa00e6e57ad 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -61,7 +61,6 @@ config USB_ARCH_HAS_EHCI
default y if PPC_83xx
default y if SOC_AU1200
default y if ARCH_IXP4XX
- default y if ARCH_TEGRA
default y if ARCH_W90X900
default y if ARCH_AT91SAM9G45
default y if ARCH_MXC
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 10f6ab5f9150..28deb1ac09b0 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -126,6 +126,14 @@ config USB_EHCI_MXC
---help---
Variation of ARC USB block used in some Freescale chips.
+config USB_EHCI_TEGRA
+ boolean "NVIDIA Tegra HCD support"
+ depends on USB_EHCI_HCD && ARCH_TEGRA
+ select USB_EHCI_ROOT_HUB_TT
+ help
+ This driver enables support for the internal USB Host Controller
+ found in NVIDIA Tegra SoCs. The Tegra controller is EHCI compliant.
+
config USB_EHCI_HCD_PPC_OF
bool "EHCI support for PPC USB controller on OF platform bus"
depends on USB_EHCI_HCD && PPC_OF
@@ -418,14 +426,6 @@ config USB_HWA_HCD
To compile this driver a module, choose M here: the module
will be called "hwa-hc".
-config USB_TEGRA_HCD
- boolean "NVIDIA Tegra HCD support"
- depends on USB && ARCH_TEGRA && USB_EHCI_HCD
- select USB_EHCI_ROOT_HUB_TT
- help
- This driver enables support for the internal USB Host Controller
- found in NVIDIA Tegra SoCs. The Tegra controller is EHCI compliant.
-
config USB_IMX21_HCD
tristate "iMX21 HCD support"
depends on USB && ARM && MACH_MX21
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 74cc97b80c4b..66505a0d9703 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -259,8 +259,7 @@ static int ehci_reset (struct ehci_hcd *ehci)
command |= CMD_RESET;
dbg_cmd (ehci, "reset", command);
- if (!ehci->controller_resets_phy)
- ehci_writel(ehci, command, &ehci->regs->command);
+ ehci_writel(ehci, command, &ehci->regs->command);
ehci_to_hcd(ehci)->state = HC_STATE_HALT;
ehci->next_statechange = jiffies;
retval = handshake (ehci, &ehci->regs->command,
@@ -1200,7 +1199,7 @@ MODULE_LICENSE ("GPL");
#ifdef CONFIG_ARCH_TEGRA
#include "ehci-tegra.c"
-#define PLATFORM_DRIVER tegra_ehci_driver
+#define PLATFORM_DRIVER tegra_ehci_driver
#endif
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 85b99f1936e1..6de0d3a5ad9f 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -14,24 +14,16 @@
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <linux/clk.h>
#include <linux/platform_device.h>
-#include <linux/tegra_usb.h>
+#include <linux/platform_data/tegra_usb.h>
#include <linux/irq.h>
#include <linux/usb/otg.h>
#include <mach/usb_phy.h>
#include <mach/iomap.h>
-#define TEGRA_USB_USBCMD_REG_OFFSET 0x140
-#define TEGRA_USB_USBCMD_RESET (1 << 1)
-#define TEGRA_USB_USBMODE_REG_OFFSET 0x1a8
-#define TEGRA_USB_USBMODE_HOST (3 << 0)
-#define TEGRA_USB_PORTSC1_PTC(x) (((x) & 0xf) << 16)
#define TEGRA_USB_PORTSC_PHCD (1 << 23)
#define TEGRA_USB_SUSP_CTRL_OFFSET 0x400
@@ -44,16 +36,6 @@
#define TEGRA_USB_DMA_ALIGN 32
-struct tegra_ehci_context {
- bool valid;
- u32 command;
- u32 frame_list;
- u32 async_next;
- u32 txfilltunning;
- u32 otgsc;
- enum tegra_usb_phy_port_speed port_speed;
-};
-
struct tegra_ehci_hcd {
struct ehci_hcd *ehci;
struct tegra_usb_phy *phy;
@@ -63,9 +45,9 @@ struct tegra_ehci_hcd {
int host_resumed;
int bus_suspended;
int port_resuming;
- struct tegra_ehci_context context;
int power_down_on_bus_suspend;
struct delayed_work work;
+ enum tegra_usb_phy_port_speed port_speed;
};
static void tegra_ehci_power_up(struct usb_hcd *hcd)
@@ -123,13 +105,13 @@ static int tegra_ehci_hub_control(
* USB_PORT_FEAT_ENABLE is handled by masking the set on clear bits
*/
if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_ENABLE) {
- temp = ehci_readl(ehci, status_reg);
- ehci_writel(ehci, (temp & ~PORT_RWC_BITS) & ~PORT_PE, status_reg);
+ temp = ehci_readl(ehci, status_reg) & ~PORT_RWC_BITS;
+ ehci_writel(ehci, temp & ~PORT_PE, status_reg);
goto done;
} else if (typeReq == GetPortStatus) {
temp = ehci_readl(ehci, status_reg);
if (tegra->port_resuming && !(temp & PORT_SUSPEND)) {
- /* resume completed */
+ /* Resume completed, re-enable disconnect detection */
tegra->port_resuming = 0;
tegra_usb_phy_postresume(tegra->phy);
}
@@ -140,29 +122,31 @@ static int tegra_ehci_hub_control(
goto done;
}
- /* After above check the port must be connected.
- * Set appropriate bit thus could put phy into low power
- * mode if we have hostpc feature
- */
temp &= ~PORT_WKCONN_E;
temp |= PORT_WKDISC_E | PORT_WKOC_E;
ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
+
+ /*
+ * If a transaction is in progress, there may be a delay in
+ * suspending the port. Poll until the port is suspended.
+ */
if (handshake(ehci, status_reg, PORT_SUSPEND,
PORT_SUSPEND, 5000))
- pr_err("%s: timeout waiting for PORT_SUSPEND\n", __func__);
+ pr_err("%s: timeout waiting for SUSPEND\n", __func__);
+
set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports);
goto done;
}
/*
- * Tegra host controller will time the resume operation to clear the bit
- * when the port control state switches to HS or FS Idle. This behavior
- * is different from EHCI where the host controller driver is required
- * to set this bit to a zero after the resume duration is timed in the
- * driver.
- */
-
- else if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_SUSPEND) {
+ * Tegra host controller will time the resume operation to clear the bit
+ * when the port control state switches to HS or FS Idle. This behavior
+ * is different from EHCI where the host controller driver is required
+ * to set this bit to a zero after the resume duration is timed in the
+ * driver.
+ */
+ else if (typeReq == ClearPortFeature &&
+ wValue == USB_PORT_FEAT_SUSPEND) {
temp = ehci_readl(ehci, status_reg);
if ((temp & PORT_RESET) || !(temp & PORT_PE)) {
retval = -EPIPE;
@@ -172,6 +156,7 @@ static int tegra_ehci_hub_control(
if (!(temp & PORT_SUSPEND))
goto done;
+ /* Disable disconnect detection during port resume */
tegra_usb_phy_preresume(tegra->phy);
ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25);
@@ -184,13 +169,11 @@ static int tegra_ehci_hub_control(
msleep(20);
spin_lock_irqsave(&ehci->lock, flags);
- /* polling PORT_RESUME until the controller clear this bit */
+ /* Poll until the controller clears RESUME and SUSPEND */
if (handshake(ehci, status_reg, PORT_RESUME, 0, 2000))
- pr_err("%s: timeout waiting for PORT_RESUME\n", __func__);
-
- /* polling PORT_SUSPEND until the controller clear this bit */
+ pr_err("%s: timeout waiting for RESUME\n", __func__);
if (handshake(ehci, status_reg, PORT_SUSPEND, 0, 2000))
- pr_err("%s: timeout waiting for PORT_SUSPEND\n", __func__);
+ pr_err("%s: timeout waiting for SUSPEND\n", __func__);
ehci->reset_done[wIndex-1] = 0;
@@ -268,18 +251,10 @@ done:
static void tegra_ehci_restart(struct usb_hcd *hcd)
{
- unsigned int temp;
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ unsigned int temp;
- /* Set to Host mode by setting bit 0-1 of USB device mode register */
- temp = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET);
- writel((temp | TEGRA_USB_USBMODE_HOST),
- (hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET));
-
- /* reset the ehci controller */
- ehci->controller_resets_phy = 0;
ehci_reset(ehci);
- ehci->controller_resets_phy = 1;
/* setup the frame list and Async q heads */
ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list);
@@ -310,7 +285,6 @@ static int tegra_usb_suspend(struct usb_hcd *hcd)
{
struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
struct ehci_regs __iomem *hw = tegra->ehci->regs;
- struct tegra_ehci_context *context = &tegra->context;
unsigned long flags;
int hsic = 0;
struct tegra_ulpi_config *config;
@@ -322,38 +296,23 @@ static int tegra_usb_suspend(struct usb_hcd *hcd)
spin_lock_irqsave(&tegra->ehci->lock, flags);
- context->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3;
-
- if ((context->port_speed > TEGRA_USB_PHY_PORT_HIGH) || hsic) {
- /* If no device connection or invalid speeds,
- * don't save the context */
- context->valid = false;
- } else {
- context->command = readl(&hw->command);
- context->frame_list = readl(&hw->frame_list);
- context->async_next = readl(&hw->async_next);
- context->txfilltunning = readl(&hw->reserved[2]);
- context->otgsc = readl(&hw->reserved[18]);
- context->valid = true;
- }
-
+ tegra->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3;
ehci_halt(tegra->ehci);
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
spin_unlock_irqrestore(&tegra->ehci->lock, flags);
- tegra_ehci_power_down(ehci_to_hcd(tegra->ehci));
+ tegra_ehci_power_down(hcd);
return 0;
}
static int tegra_usb_resume(struct usb_hcd *hcd)
{
struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
- struct tegra_ehci_context *context = &tegra->context;
struct usb_device *udev = hcd->self.root_hub;
- struct ehci_regs __iomem *hw = tegra->ehci->regs;
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct ehci_regs __iomem *hw = ehci->regs;
unsigned long val;
- int lp0_resume = 0;
int hsic = 0;
struct tegra_ulpi_config *config;
@@ -361,30 +320,22 @@ static int tegra_usb_resume(struct usb_hcd *hcd)
config = tegra->phy->config;
hsic = (config->inf_type == TEGRA_USB_UHSIC);
}
- tegra_ehci_power_up(ehci_to_hcd(tegra->ehci));
+
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ tegra_ehci_power_up(hcd);
- if (!context->valid) {
+ if (tegra->port_speed > TEGRA_USB_PHY_PORT_SPEED_HIGH) {
/* Wait for the phy to detect new devices
* before we restart the controller */
msleep(10);
goto restart;
}
- tegra_ehci_phy_restore_start(tegra->phy, context->port_speed);
+ /* Force the phy to keep data lines in suspend state */
+ tegra_ehci_phy_restore_start(tegra->phy, tegra->port_speed);
- /* Check if the phy resume from LP0. When the phy resume from LP0
- * USB register will be reset. */
- if (!readl(&hw->async_next))
- lp0_resume = 1;
-
- /* Restore register context */
- writel(TEGRA_USB_USBMODE_HOST, &hw->reserved[19]);
- writel(context->otgsc, &hw->reserved[18]);
- writel(context->txfilltunning, &hw->reserved[2]);
- writel(context->async_next, &hw->async_next);
- writel(context->frame_list, &hw->frame_list);
- writel(context->command, &hw->command);
+ /* Enable host mode */
+ tdi_reset(ehci);
/* Enable Port Power */
val = readl(&hw->port_status[0]);
@@ -392,36 +343,38 @@ static int tegra_usb_resume(struct usb_hcd *hcd)
writel(val, &hw->port_status[0]);
udelay(10);
- if (lp0_resume) {
- /* Program the field PTC in PORTSC based on the saved speed mode */
+ /* Check if the phy resume from LP0. When the phy resume from LP0
+ * USB register will be reset. */
+ if (!readl(&hw->async_next)) {
+ /* Program the field PTC based on the saved speed mode */
val = readl(&hw->port_status[0]);
- val &= ~(TEGRA_USB_PORTSC1_PTC(~0));
- if (context->port_speed == TEGRA_USB_PHY_PORT_HIGH)
- val |= TEGRA_USB_PORTSC1_PTC(5);
- else if (context->port_speed == TEGRA_USB_PHY_PORT_SPEED_FULL)
- val |= TEGRA_USB_PORTSC1_PTC(6);
- else if (context->port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW)
- val |= TEGRA_USB_PORTSC1_PTC(7);
+ val &= ~PORT_TEST(~0);
+ if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_HIGH)
+ val |= PORT_TEST_FORCE;
+ else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_FULL)
+ val |= PORT_TEST(6);
+ else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW)
+ val |= PORT_TEST(7);
writel(val, &hw->port_status[0]);
udelay(10);
- }
- /* Disable test mode by setting PTC field to NORMAL_OP */
- val = readl(&hw->port_status[0]);
- val &= ~(TEGRA_USB_PORTSC1_PTC(~0));
- writel(val, &hw->port_status[0]);
- udelay(10);
+ /* Disable test mode by setting PTC field to NORMAL_OP */
+ val = readl(&hw->port_status[0]);
+ val &= ~PORT_TEST(~0);
+ writel(val, &hw->port_status[0]);
+ udelay(10);
+ }
/* Poll until CCS is enabled */
- if (handshake(tegra->ehci, &hw->port_status[0], PORT_CONNECT,
- PORT_CONNECT, 2000)) {
+ if (handshake(ehci, &hw->port_status[0], PORT_CONNECT,
+ PORT_CONNECT, 2000)) {
pr_err("%s: timeout waiting for PORT_CONNECT\n", __func__);
goto restart;
}
/* Poll until PE is enabled */
- if (handshake(tegra->ehci, &hw->port_status[0], PORT_PE,
- PORT_PE, 2000)) {
+ if (handshake(ehci, &hw->port_status[0], PORT_PE,
+ PORT_PE, 2000)) {
pr_err("%s: timeout waiting for USB_PORTSC1_PE\n", __func__);
goto restart;
}
@@ -438,8 +391,8 @@ static int tegra_usb_resume(struct usb_hcd *hcd)
writel(val, &hw->port_status[0]);
/* Wait until port suspend completes */
- if (handshake(tegra->ehci, &hw->port_status[0], PORT_SUSPEND,
- PORT_SUSPEND, 1000)) {
+ if (handshake(ehci, &hw->port_status[0], PORT_SUSPEND,
+ PORT_SUSPEND, 1000)) {
pr_err("%s: timeout waiting for PORT_SUSPEND\n",
__func__);
goto restart;
@@ -447,11 +400,10 @@ static int tegra_usb_resume(struct usb_hcd *hcd)
}
tegra_ehci_phy_restore_end(tegra->phy);
-
return 0;
restart:
- if (context->valid)
+ if (tegra->port_speed <= TEGRA_USB_PHY_PORT_SPEED_HIGH)
tegra_ehci_phy_restore_end(tegra->phy);
if (hsic) {
val = readl(&hw->port_status[0]);
@@ -469,43 +421,15 @@ restart:
return 0;
}
-static int tegra_ehci_reset(struct usb_hcd *hcd)
-{
- unsigned long temp;
- int usec = 250*1000; /* see ehci_reset */
-
- temp = readl(hcd->regs + TEGRA_USB_USBCMD_REG_OFFSET);
- temp |= TEGRA_USB_USBCMD_RESET;
- writel(temp, hcd->regs + TEGRA_USB_USBCMD_REG_OFFSET);
-
- do {
- temp = readl(hcd->regs + TEGRA_USB_USBCMD_REG_OFFSET);
- if (!(temp & TEGRA_USB_USBCMD_RESET))
- break;
- udelay(1);
- usec--;
- } while (usec);
-
- if (!usec)
- return -ETIMEDOUT;
-
- /* Set to Host mode by setting bit 0-1 of USB device mode register */
- temp = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET);
- writel((temp | TEGRA_USB_USBMODE_HOST),
- (hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET));
-
- return 0;
-}
-
static void tegra_ehci_shutdown(struct usb_hcd *hcd)
{
struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
+
/* ehci_shutdown touches the USB controller registers, make sure
* controller has clocks to it */
if (!tegra->host_resumed)
tegra_ehci_power_up(hcd);
- /* call ehci shut down */
ehci_shutdown(hcd);
/* we are ready to shut down, powerdown the phy */
@@ -528,6 +452,10 @@ static int tegra_ehci_setup(struct usb_hcd *hcd)
/* cache this readonly data; minimize chip reads */
ehci->hcs_params = readl(&ehci->caps->hcs_params);
+ /* switch to host mode */
+ hcd->has_tt = 1;
+ ehci_reset(ehci);
+
retval = ehci_halt(ehci);
if (retval)
return retval;
@@ -537,19 +465,8 @@ static int tegra_ehci_setup(struct usb_hcd *hcd)
if (retval)
return retval;
- hcd->has_tt = 1;
ehci->sbrn = 0x20;
- ehci_reset(ehci);
-
- /*
- * Resetting the controller has the side effect of resetting the PHY.
- * So, never reset the controller after the calling
- * tegra_ehci_reinit API.
- */
- ehci->controller_resets_phy = 1;
- ehci->port_reset_no_wait = 1;
-
ehci_port_power(ehci, 1);
return retval;
}
@@ -855,10 +772,8 @@ static int tegra_ehci_probe(struct platform_device *pdev)
{
struct resource *res;
struct usb_hcd *hcd;
- struct ehci_hcd *ehci;
struct tegra_ehci_hcd *tegra;
struct tegra_ehci_platform_data *pdata;
- struct tegra_utmip_config *config;
int err = 0;
int irq;
int instance = pdev->id;
@@ -919,11 +834,9 @@ static int tegra_ehci_probe(struct platform_device *pdev)
goto fail_io;
}
- config = pdata->phy_config;
-
INIT_DELAYED_WORK(&tegra->work, tegra_hsic_connection_work);
- tegra->phy = tegra_usb_phy_open(instance, hcd->regs, config,
+ tegra->phy = tegra_usb_phy_open(instance, hcd->regs, pdata->phy_config,
TEGRA_USB_PHY_MODE_HOST);
if (IS_ERR(tegra->phy)) {
dev_err(&pdev->dev, "Failed to open USB phy\n");
@@ -931,15 +844,15 @@ static int tegra_ehci_probe(struct platform_device *pdev)
goto fail_phy;
}
- err = tegra_ehci_reset(hcd);
+ err = tegra_usb_phy_power_on(tegra->phy);
if (err) {
- dev_err(&pdev->dev, "Failed to reset controller\n");
+ dev_err(&pdev->dev, "Failed to power on the phy\n");
goto fail;
}
- tegra_usb_phy_power_on(tegra->phy);
tegra->host_resumed = 1;
tegra->power_down_on_bus_suspend = pdata->power_down_on_bus_suspend;
+ tegra->ehci = hcd_to_ehci(hcd);
irq = platform_get_irq(pdev, 0);
if (!irq) {
@@ -947,18 +860,15 @@ static int tegra_ehci_probe(struct platform_device *pdev)
err = -ENODEV;
goto fail;
}
-
set_irq_flags(irq, IRQF_VALID);
- ehci = hcd_to_ehci(hcd);
- tegra->ehci = ehci;
-
#ifdef CONFIG_USB_EHCI_ONOFF_FEATURE
if (instance == 1) {
ehci_tegra_irq = irq;
- create_ehci_sys_file(ehci);
+ create_ehci_sys_file(tegra->ehci);
}
#endif
+
#ifdef CONFIG_USB_OTG_UTILS
if (pdata->operating_mode == TEGRA_USB_OTG) {
tegra->transceiver = otg_get_transceiver();
@@ -968,7 +878,7 @@ static int tegra_ehci_probe(struct platform_device *pdev)
#endif
err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
- if (err != 0) {
+ if (err) {
dev_err(&pdev->dev, "Failed to add USB HCD\n");
goto fail;
}
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 530540a4bdd4..6f62e68e0e6f 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -132,7 +132,6 @@ struct ehci_hcd { /* one per controller */
unsigned need_io_watchdog:1;
unsigned broken_periodic:1;
unsigned fs_i_thresh:1; /* Intel iso scheduling */
- unsigned controller_resets_phy:1;
unsigned port_reset_no_wait:1;
/* required for usb32 quirk */
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index 2240602fc81b..5ea7329e7f98 100644
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -49,6 +49,13 @@ config USB_ULPI
Enable this to support ULPI connected USB OTG transceivers which
are likely found on embedded boards.
+config USB_ULPI_VIEWPORT
+ bool
+ depends on USB_ULPI
+ help
+ Provides read/write operations to the ULPI phy register set for
+ controllers with a viewport register (e.g. Chipidea/ARC controllers).
+
config TWL4030_USB
tristate "TWL4030 USB Transceiver Driver"
depends on TWL4030_CORE && REGULATOR_TWL4030
diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile
index fbf2a25a2e8c..8ca50cf114f2 100644
--- a/drivers/usb/otg/Makefile
+++ b/drivers/usb/otg/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o
obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o
obj-$(CONFIG_USB_ULPI) += ulpi.o
+obj-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi_viewport.o
ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG
ccflags-$(CONFIG_USB_GADGET_DEBUG) += -DDEBUG
diff --git a/drivers/usb/otg/tegra-otg.c b/drivers/usb/otg/tegra-otg.c
index 5230f9f6ee82..b4804c022098 100644
--- a/drivers/usb/otg/tegra-otg.c
+++ b/drivers/usb/otg/tegra-otg.c
@@ -26,7 +26,7 @@
#include <linux/usb/gadget.h>
#include <linux/usb/hcd.h>
#include <linux/platform_device.h>
-#include <linux/tegra_usb.h>
+#include <linux/platform_data/tegra_usb.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/delay.h>
@@ -339,7 +339,7 @@ static int __exit tegra_otg_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
-static int tegra_otg_suspend(struct platform_device *pdev)
+static int tegra_otg_suspend(struct platform_device *pdev, pm_message_t state)
{
struct tegra_otg_data *tegra_otg = platform_get_drvdata(pdev);
diff --git a/drivers/usb/otg/ulpi_viewport.c b/drivers/usb/otg/ulpi_viewport.c
new file mode 100644
index 000000000000..e9a37f90994f
--- /dev/null
+++ b/drivers/usb/otg/ulpi_viewport.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/io.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/ulpi.h>
+
+#define ULPI_VIEW_WAKEUP (1 << 31)
+#define ULPI_VIEW_RUN (1 << 30)
+#define ULPI_VIEW_WRITE (1 << 29)
+#define ULPI_VIEW_READ (0 << 29)
+#define ULPI_VIEW_ADDR(x) (((x) & 0xff) << 16)
+#define ULPI_VIEW_DATA_READ(x) (((x) >> 8) & 0xff)
+#define ULPI_VIEW_DATA_WRITE(x) ((x) & 0xff)
+
+static int ulpi_viewport_wait(void __iomem *view, u32 mask)
+{
+ unsigned long usec = 2000;
+
+ while (usec--) {
+ if (!(readl(view) & mask))
+ return 0;
+
+ udelay(1);
+ };
+
+ return -ETIMEDOUT;
+}
+
+static int ulpi_viewport_read(struct otg_transceiver *otg, u32 reg)
+{
+ int ret;
+ void __iomem *view = otg->io_priv;
+
+ writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view);
+ ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP);
+ if (ret)
+ return ret;
+
+ writel(ULPI_VIEW_RUN | ULPI_VIEW_READ | ULPI_VIEW_ADDR(reg), view);
+ ret = ulpi_viewport_wait(view, ULPI_VIEW_RUN);
+ if (ret)
+ return ret;
+
+ return ULPI_VIEW_DATA_READ(readl(view));
+}
+
+static int ulpi_viewport_write(struct otg_transceiver *otg, u32 val, u32 reg)
+{
+ int ret;
+ void __iomem *view = otg->io_priv;
+
+ writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view);
+ ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP);
+ if (ret)
+ return ret;
+
+ writel(ULPI_VIEW_RUN | ULPI_VIEW_WRITE | ULPI_VIEW_DATA_WRITE(val) |
+ ULPI_VIEW_ADDR(reg), view);
+
+ return ulpi_viewport_wait(view, ULPI_VIEW_RUN);
+}
+
+struct otg_io_access_ops ulpi_viewport_access_ops = {
+ .read = ulpi_viewport_read,
+ .write = ulpi_viewport_write,
+};
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index ebd9f1830f53..403c8b786212 100755
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -1163,6 +1163,9 @@ static bool _tegra_dc_controller_enable(struct tegra_dc *dc)
clk_enable(dc->clk);
clk_enable(dc->emc_clk);
+ tegra_periph_reset_deassert(dc->clk);
+ msleep(10);
+
enable_irq(dc->irq);
tegra_dc_init(dc);
diff --git a/drivers/video/tegra/dc/hdmi.c b/drivers/video/tegra/dc/hdmi.c
index 71e892ac57d0..f705d842a09c 100644
--- a/drivers/video/tegra/dc/hdmi.c
+++ b/drivers/video/tegra/dc/hdmi.c
@@ -551,7 +551,6 @@ static void tegra_dc_hdmi_suspend(struct tegra_dc *dc)
tegra_nvhdcp_suspend(hdmi->nvhdcp);
spin_lock_irqsave(&hdmi->suspend_lock, flags);
- tegra_nvhdcp_suspend(hdmi->nvhdcp);
hdmi->suspended = true;
spin_unlock_irqrestore(&hdmi->suspend_lock, flags);
}
@@ -639,6 +638,7 @@ static int tegra_dc_hdmi_init(struct tegra_dc *dc)
err = -EBUSY;
goto err_put_clock;
}
+ enable_irq_wake(gpio_to_irq(dc->out->hotplug_gpio));
hdmi->edid = tegra_edid_create(dc->out->dcc_bus);
if (IS_ERR_OR_NULL(hdmi->edid)) {
@@ -687,6 +687,7 @@ static int tegra_dc_hdmi_init(struct tegra_dc *dc)
err_edid_destroy:
tegra_edid_destroy(hdmi->edid);
err_free_irq:
+ disable_irq_wake(gpio_to_irq(dc->out->hotplug_gpio));
free_irq(gpio_to_irq(dc->out->hotplug_gpio), dc);
err_put_clock:
if (!IS_ERR_OR_NULL(disp2_clk))
@@ -708,6 +709,7 @@ static void tegra_dc_hdmi_destroy(struct tegra_dc *dc)
{
struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc);
+ disable_irq_wake(gpio_to_irq(dc->out->hotplug_gpio));
free_irq(gpio_to_irq(dc->out->hotplug_gpio), dc);
cancel_delayed_work_sync(&hdmi->work);
switch_dev_unregister(&hdmi->hpd_switch);
@@ -891,7 +893,7 @@ static void tegra_dc_hdmi_setup_avi_infoframe(struct tegra_dc *dc, bool dvi)
avi.vic = 4; /* 60 Hz */
else
avi.vic = 19; /* 50 Hz */
- } else if (dc->mode.v_active == 720) {
+ } else if (dc->mode.v_active == 1080) {
avi.m = HDMI_AVI_M_16_9;
if (dc->mode.h_front_porch == 88)
avi.vic = 16; /* 60 Hz */
diff --git a/include/linux/platform_data/tegra_usb.h b/include/linux/platform_data/tegra_usb.h
new file mode 100644
index 000000000000..e48ac43dd047
--- /dev/null
+++ b/include/linux/platform_data/tegra_usb.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _TEGRA_USB_H_
+#define _TEGRA_USB_H_
+
+enum tegra_usb_operating_modes {
+ TEGRA_USB_DEVICE,
+ TEGRA_USB_HOST,
+ TEGRA_USB_OTG,
+};
+
+struct tegra_ehci_platform_data {
+ enum tegra_usb_operating_modes operating_mode;
+ /* power down the phy on bus suspend */
+ int power_down_on_bus_suspend;
+ void *phy_config;
+};
+
+struct tegra_otg_platform_data {
+ struct platform_device* (*host_register)(void);
+ void (*host_unregister)(struct platform_device*);
+};
+
+#endif /* _TEGRA_USB_H_ */
diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h
index 2e262cb15425..656380245198 100644
--- a/include/linux/usb/ehci_def.h
+++ b/include/linux/usb/ehci_def.h
@@ -127,7 +127,9 @@ struct ehci_regs {
#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */
/* 19:16 for port testing */
-#define PORT_TEST_PKT (0x4<<16) /* Port Test Control - packet test */
+#define PORT_TEST(x) (((x)&0xf)<<16) /* Port Test Control */
+#define PORT_TEST_PKT PORT_TEST(0x4) /* Port Test Control - packet test */
+#define PORT_TEST_FORCE PORT_TEST(0x5) /* Port Test Control - force enable */
#define PORT_LED_OFF (0<<14)
#define PORT_LED_AMBER (1<<14)
#define PORT_LED_GREEN (2<<14)
diff --git a/include/linux/usb/ulpi.h b/include/linux/usb/ulpi.h
index 82b1507f4735..9595796d62ed 100644
--- a/include/linux/usb/ulpi.h
+++ b/include/linux/usb/ulpi.h
@@ -184,4 +184,9 @@
struct otg_transceiver *otg_ulpi_create(struct otg_io_access_ops *ops,
unsigned int flags);
+#ifdef CONFIG_USB_ULPI_VIEWPORT
+/* access ops for controllers with a viewport register */
+extern struct otg_io_access_ops ulpi_viewport_access_ops;
+#endif
+
#endif /* __LINUX_USB_ULPI_H */