mirror of
https://github.com/torvalds/linux.git
synced 2026-06-10 15:42:19 +02:00
Merge branch 'linux-tegra-2.6.36' into android-tegra-2.6.36
This commit is contained in:
commit
4f72bb8edd
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -17,10 +17,8 @@
|
|||
#ifndef __MACH_USB_PHY_H
|
||||
#define __MACH_USB_PHY_H
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#define USB_PHY_MAX_CONTEXT_REGS 10
|
||||
#include <linux/usb/otg.h>
|
||||
|
||||
struct tegra_utmip_config {
|
||||
u8 hssync_start_delay;
|
||||
|
|
@ -40,7 +38,7 @@ struct tegra_ulpi_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 {
|
||||
|
|
@ -48,9 +46,11 @@ enum tegra_usb_phy_mode {
|
|||
TEGRA_USB_PHY_MODE_HOST,
|
||||
};
|
||||
|
||||
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;
|
||||
|
|
@ -58,6 +58,7 @@ struct tegra_usb_phy {
|
|||
struct clk *pad_clk;
|
||||
enum tegra_usb_phy_mode mode;
|
||||
void *config;
|
||||
struct otg_transceiver *ulpi;
|
||||
};
|
||||
|
||||
struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
|
||||
|
|
@ -65,21 +66,21 @@ 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);
|
||||
|
||||
#endif //__MACH_USB_PHY_H
|
||||
#endif /* __MACH_USB_PHY_H */
|
||||
|
|
|
|||
|
|
@ -25,22 +25,13 @@
|
|||
#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>
|
||||
|
||||
#define USB_USBSTS 0x144
|
||||
#define USB_USBSTS_PCI (1 << 2)
|
||||
|
||||
#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)
|
||||
|
|
@ -150,26 +141,48 @@
|
|||
static DEFINE_SPINLOCK(utmip_pad_lock);
|
||||
static int utmip_pad_count;
|
||||
|
||||
static const int udc_freq_table[] = {
|
||||
12000000,
|
||||
13000000,
|
||||
19200000,
|
||||
26000000,
|
||||
struct tegra_xtal_freq {
|
||||
int freq;
|
||||
u8 enable_delay;
|
||||
u8 stable_count;
|
||||
u8 active_delay;
|
||||
u8 xtal_freq_count;
|
||||
u16 debounce;
|
||||
};
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
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[] = {
|
||||
|
|
@ -193,12 +206,17 @@ static struct tegra_utmip_config utmip_default[] = {
|
|||
},
|
||||
};
|
||||
|
||||
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) {
|
||||
|
|
@ -248,7 +266,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);
|
||||
|
|
@ -335,7 +353,7 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy)
|
|||
pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -368,7 +386,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);
|
||||
|
|
@ -377,14 +395,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) {
|
||||
|
|
@ -457,6 +475,8 @@ static void utmi_phy_power_on(struct tegra_usb_phy *phy)
|
|||
val &= ~USB_PORTSC1_PTS(~0);
|
||||
writel(val, base + USB_PORTSC1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void utmi_phy_power_off(struct tegra_usb_phy *phy)
|
||||
|
|
@ -546,21 +566,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;
|
||||
|
|
@ -598,17 +606,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;
|
||||
|
|
@ -622,6 +631,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)
|
||||
|
|
@ -647,7 +658,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);
|
||||
|
|
@ -660,7 +671,7 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
|
|||
phy->mode = phy_mode;
|
||||
|
||||
if (!phy->config) {
|
||||
if (instance == 1) {
|
||||
if (phy_is_ulpi(phy)) {
|
||||
pr_err("%s: ulpi phy configuration missing", __func__);
|
||||
err = -EINVAL;
|
||||
goto err0;
|
||||
|
|
@ -678,18 +689,19 @@ 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;
|
||||
|
||||
if (phy->instance == 1) {
|
||||
if (phy_is_ulpi(phy)) {
|
||||
ulpi_config = config;
|
||||
phy->clk = clk_get_sys(NULL, ulpi_config->clk);
|
||||
if (IS_ERR(phy->clk)) {
|
||||
|
|
@ -700,6 +712,8 @@ 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);
|
||||
if (err < 0)
|
||||
|
|
@ -718,77 +732,64 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
|
|||
|
||||
int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
|
||||
{
|
||||
if (phy->instance == 1)
|
||||
ulpi_phy_power_on(phy);
|
||||
if (phy_is_ulpi(phy))
|
||||
return ulpi_phy_power_on(phy);
|
||||
else
|
||||
utmi_phy_power_on(phy);
|
||||
|
||||
return 0;
|
||||
return utmi_phy_power_on(phy);
|
||||
}
|
||||
|
||||
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))
|
||||
ulpi_phy_power_off(phy);
|
||||
else
|
||||
utmi_phy_power_off(phy);
|
||||
|
||||
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))
|
||||
clk_put(phy->clk);
|
||||
else
|
||||
utmip_pad_close(phy);
|
||||
clk_disable(phy->pll_u);
|
||||
clk_put(phy->pll_u);
|
||||
kfree(phy);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -318,6 +318,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;
|
||||
|
|
@ -341,11 +386,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;
|
||||
|
|
@ -558,7 +600,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;
|
||||
|
||||
flush_work(&t->tx_work);
|
||||
|
|
@ -572,11 +613,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;
|
||||
|
|
@ -603,7 +640,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");
|
||||
|
|
@ -624,20 +660,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:
|
||||
|
|
@ -659,6 +681,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);
|
||||
|
|
@ -667,7 +690,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)) {
|
||||
|
|
@ -995,8 +1018,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);
|
||||
}
|
||||
|
|
@ -1020,10 +1045,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;
|
||||
|
|
@ -1067,6 +1088,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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -1198,9 +1197,9 @@ MODULE_LICENSE ("GPL");
|
|||
#define PLATFORM_DRIVER ehci_atmel_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA
|
||||
#ifdef CONFIG_USB_EHCI_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) && \
|
||||
|
|
|
|||
|
|
@ -14,36 +14,17 @@
|
|||
* 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>
|
||||
|
||||
#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_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;
|
||||
|
|
@ -53,8 +34,8 @@ struct tegra_ehci_hcd {
|
|||
int host_resumed;
|
||||
int bus_suspended;
|
||||
int port_resuming;
|
||||
struct tegra_ehci_context context;
|
||||
int power_down_on_bus_suspend;
|
||||
enum tegra_usb_phy_port_speed port_speed;
|
||||
};
|
||||
|
||||
static void tegra_ehci_power_up(struct usb_hcd *hcd)
|
||||
|
|
@ -103,15 +84,15 @@ 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);
|
||||
}
|
||||
|
|
@ -124,16 +105,18 @@ 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;
|
||||
}
|
||||
|
|
@ -145,7 +128,8 @@ static int tegra_ehci_hub_control(
|
|||
* 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) {
|
||||
else if (typeReq == ClearPortFeature &&
|
||||
wValue == USB_PORT_FEAT_SUSPEND) {
|
||||
temp = ehci_readl(ehci, status_reg);
|
||||
if ((temp & PORT_RESET) || !(temp & PORT_PE)) {
|
||||
retval = -EPIPE;
|
||||
|
|
@ -155,6 +139,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);
|
||||
|
|
@ -167,13 +152,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;
|
||||
|
||||
|
|
@ -192,18 +175,9 @@ static int tegra_ehci_hub_control(
|
|||
|
||||
static void tegra_ehci_restart(struct usb_hcd *hcd)
|
||||
{
|
||||
unsigned int temp;
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
|
||||
/* reset the ehci controller */
|
||||
ehci->controller_resets_phy = 0;
|
||||
ehci_reset(ehci);
|
||||
ehci->controller_resets_phy = 1;
|
||||
|
||||
/* 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));
|
||||
|
||||
/* setup the frame list and Async q heads */
|
||||
ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list);
|
||||
|
|
@ -224,67 +198,42 @@ 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;
|
||||
|
||||
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) {
|
||||
/* 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 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;
|
||||
|
||||
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
tegra_ehci_power_up(ehci_to_hcd(tegra->ehci));
|
||||
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]);
|
||||
|
|
@ -292,36 +241,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 &= ~PORT_TEST(~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 &= ~(TEGRA_USB_PORTSC1_PTC(~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;
|
||||
}
|
||||
|
|
@ -338,8 +289,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;
|
||||
|
|
@ -347,54 +298,25 @@ 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);
|
||||
|
||||
tegra_ehci_restart(hcd);
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -414,6 +336,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;
|
||||
|
|
@ -423,19 +349,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;
|
||||
}
|
||||
|
|
@ -591,10 +506,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;
|
||||
|
|
@ -655,9 +568,7 @@ static int tegra_ehci_probe(struct platform_device *pdev)
|
|||
goto fail_io;
|
||||
}
|
||||
|
||||
config = pdata->phy_config;
|
||||
|
||||
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");
|
||||
|
|
@ -665,15 +576,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) {
|
||||
|
|
@ -681,12 +592,8 @@ 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_OTG_UTILS
|
||||
if (pdata->operating_mode == TEGRA_USB_OTG) {
|
||||
tegra->transceiver = otg_get_transceiver();
|
||||
|
|
@ -696,7 +603,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
80
drivers/usb/otg/ulpi_viewport.c
Normal file
80
drivers/usb/otg/ulpi_viewport.c
Normal file
|
|
@ -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,
|
||||
};
|
||||
31
include/linux/platform_data/tegra_usb.h
Normal file
31
include/linux/platform_data/tegra_usb.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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;
|
||||
};
|
||||
|
||||
#endif /* _TEGRA_USB_H_ */
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#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;
|
||||
};
|
||||
|
||||
#endif /* _TEGRA_USB_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)
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user