diff options
-rw-r--r-- | arch/arm/mach-tegra/Kconfig | 3 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/system.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/usb_phy.h | 26 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra2_emc.c | 85 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra2_emc.h | 13 | ||||
-rw-r--r-- | arch/arm/mach-tegra/usb_phy.c | 195 | ||||
-rw-r--r-- | drivers/serial/tegra_hsuart.c | 87 | ||||
-rw-r--r-- | drivers/usb/Kconfig | 1 | ||||
-rw-r--r-- | drivers/usb/host/Kconfig | 16 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 5 | ||||
-rw-r--r-- | drivers/usb/host/ehci-tegra.c | 234 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 1 | ||||
-rw-r--r-- | drivers/usb/otg/Kconfig | 7 | ||||
-rw-r--r-- | drivers/usb/otg/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/otg/tegra-otg.c | 4 | ||||
-rw-r--r-- | drivers/usb/otg/ulpi_viewport.c | 80 | ||||
-rwxr-xr-x | drivers/video/tegra/dc/dc.c | 3 | ||||
-rw-r--r-- | drivers/video/tegra/dc/hdmi.c | 6 | ||||
-rw-r--r-- | include/linux/platform_data/tegra_usb.h | 36 | ||||
-rw-r--r-- | include/linux/usb/ehci_def.h | 4 | ||||
-rw-r--r-- | include/linux/usb/ulpi.h | 5 |
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 */ |