summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorSteve Lin <stlin@nvidia.com>2011-01-11 17:09:22 -0800
committerNiket Sirsi <nsirsi@nvidia.com>2011-01-20 15:36:31 -0800
commitd3f13409174432bbbb8a94e1f29ed73f72f75d84 (patch)
tree9e9e69cac51eebc39e40adec1571fd4433ec439b /arch
parentb1072c3d8f4802d06ba6d94d742e811382a17844 (diff)
[ARM] tegra: add ULPI Null PHY support
Adding ULPI Null PHY support in tegra usb driver. Bug 776276 Change-Id: Ib2f25b67828f3f8128e1868b4ce9bd8bddcef22b Reviewed-on: http://git-master/r/15433 Tested-by: Szming Lin <stlin@nvidia.com> Reviewed-by: Udaykumar Rameshchan Raval <uraval@nvidia.com> Reviewed-by: Seshendra Gadagottu <sgadagottu@nvidia.com> Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-tegra/include/mach/usb_phy.h16
-rw-r--r--arch/arm/mach-tegra/usb_phy.c171
2 files changed, 170 insertions, 17 deletions
diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h
index bb16019256ec..4caa610b619a 100644
--- a/arch/arm/mach-tegra/include/mach/usb_phy.h
+++ b/arch/arm/mach-tegra/include/mach/usb_phy.h
@@ -32,9 +32,25 @@ struct tegra_utmip_config {
u8 xcvr_lsrslew;
};
+enum tegra_ulpi_inf_type {
+ TEGRA_USB_LINK_ULPI = 0,
+ TEGRA_USB_NULL_ULPI,
+};
+
+struct tegra_ulpi_trimmer {
+ u8 shadow_clk_delay; /* 0 ~ 31 */
+ u8 clock_out_delay; /* 0 ~ 31 */
+ u8 data_trimmer; /* 0 ~ 7 */
+ u8 stpdirnxt_trimmer; /* 0 ~ 7 */
+};
+
struct tegra_ulpi_config {
+ enum tegra_ulpi_inf_type inf_type;
int reset_gpio;
const char *clk;
+ const struct tegra_ulpi_trimmer *trimmer;
+ int (*preinit)(void);
+ int (*postinit)(void);
};
enum tegra_usb_phy_port_speed {
diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c
index 7e00e77b40bd..cb87ad6934e2 100644
--- a/arch/arm/mach-tegra/usb_phy.c
+++ b/arch/arm/mach-tegra/usb_phy.c
@@ -78,9 +78,28 @@
#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD (2 << 1)
#define USB1_VBUS_SENSE_CTL_A_SESS_VLD (3 << 1)
+#define ULPIS2S_CTRL 0x418
+#define ULPIS2S_ENA (1 << 0)
+#define ULPIS2S_SUPPORT_DISCONNECT (1 << 2)
+#define ULPIS2S_PLLU_MASTER_BLASTER60 (1 << 3)
+#define ULPIS2S_SPARE(x) (((x) & 0xF) << 8)
+#define ULPIS2S_FORCE_ULPI_CLK_OUT (1 << 12)
+#define ULPIS2S_DISCON_DONT_CHECK_SE0 (1 << 13)
+#define ULPIS2S_SUPPORT_HS_KEEP_ALIVE (1 << 14)
+#define ULPIS2S_DISABLE_STP_PU (1 << 15)
+
#define ULPI_TIMING_CTRL_0 0x424
+#define ULPI_CLOCK_OUT_DELAY(x) ((x) & 0x1F)
#define ULPI_OUTPUT_PINMUX_BYP (1 << 10)
#define ULPI_CLKOUT_PINMUX_BYP (1 << 11)
+#define ULPI_SHADOW_CLK_LOOPBACK_EN (1 << 12)
+#define ULPI_SHADOW_CLK_SEL (1 << 13)
+#define ULPI_CORE_CLK_SEL (1 << 14)
+#define ULPI_SHADOW_CLK_DELAY(x) (((x) & 0x1F) << 16)
+#define ULPI_LBK_PAD_EN (1 << 26)
+#define ULPI_LBK_PAD_E_INPUT_OR (1 << 27)
+#define ULPI_CLK_OUT_ENA (1 << 28)
+#define ULPI_CLK_PADOUT_ENA (1 << 29)
#define ULPI_TIMING_CTRL_1 0x428
#define ULPI_DATA_TRIMMER_LOAD (1 << 0)
@@ -650,6 +669,108 @@ static void ulpi_phy_power_off(struct tegra_usb_phy *phy)
clk_disable(phy->clk);
}
+static void null_phy_power_on(struct tegra_usb_phy *phy)
+{
+ const struct tegra_ulpi_trimmer default_trimmer = {0, 0, 4, 4};
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ struct tegra_ulpi_config *config = phy->config;
+
+ if (config->preinit)
+ config->preinit();
+
+ if (!config->trimmer)
+ config->trimmer = &default_trimmer;
+
+ val = readl(base + USB_SUSP_CTRL);
+ val |= UHSIC_RESET;
+ writel(val, base + USB_SUSP_CTRL);
+
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val |= ULPI_PHY_ENABLE;
+ writel(val, base + USB_SUSP_CTRL);
+
+ /* set timming parameters */
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_SHADOW_CLK_LOOPBACK_EN;
+ val |= ULPI_SHADOW_CLK_SEL;
+ val |= ULPI_OUTPUT_PINMUX_BYP;
+ val |= ULPI_CLKOUT_PINMUX_BYP;
+ val |= ULPI_LBK_PAD_EN;
+ val |= ULPI_SHADOW_CLK_DELAY(config->trimmer->shadow_clk_delay);
+ val |= ULPI_CLOCK_OUT_DELAY(config->trimmer->clock_out_delay);
+ val |= ULPI_LBK_PAD_E_INPUT_OR;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+
+ val = 0;
+ writel(val, base + ULPI_TIMING_CTRL_1);
+ udelay(10);
+
+ /* enable null phy mode */
+ val = ULPIS2S_ENA;
+ val |= ULPIS2S_PLLU_MASTER_BLASTER60;
+ val |= ULPIS2S_SPARE((phy->mode == TEGRA_USB_PHY_MODE_HOST)? 3:1);
+ writel(val, base + ULPIS2S_CTRL);
+
+ /* select ULPI_CORE_CLK_SEL to SHADOW_CLK */
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_CORE_CLK_SEL;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+ udelay(10);
+
+ /* enable ULPI null clocks - can't set the trimmers before this */
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_CLK_OUT_ENA;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+ udelay(10);
+
+ val = ULPI_DATA_TRIMMER_SEL(config->trimmer->data_trimmer);
+ val |= ULPI_STPDIRNXT_TRIMMER_SEL(config->trimmer->stpdirnxt_trimmer);
+ val |= ULPI_DIR_TRIMMER_SEL(4);
+ writel(val, base + ULPI_TIMING_CTRL_1);
+ udelay(10);
+
+ val |= ULPI_DATA_TRIMMER_LOAD;
+ val |= ULPI_STPDIRNXT_TRIMMER_LOAD;
+ val |= ULPI_DIR_TRIMMER_LOAD;
+ writel(val, base + ULPI_TIMING_CTRL_1);
+
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_CLK_PADOUT_ENA;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+ udelay(10);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val |= USB_SUSP_CLR;
+ writel(val, base + USB_SUSP_CTRL);
+ udelay(100);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val &= ~USB_SUSP_CLR;
+ writel(val, base + USB_SUSP_CTRL);
+
+ if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+ USB_PHY_CLK_VALID))
+ pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
+
+ if (config->postinit)
+ config->postinit();
+}
+
+static void null_phy_power_off(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val &= ~ULPI_CLK_PADOUT_ENA;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+}
+
struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
void *config, enum tegra_usb_phy_mode phy_mode)
{
@@ -701,15 +822,18 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
if (phy->instance == 1) {
ulpi_config = config;
- phy->clk = clk_get_sys(NULL, ulpi_config->clk);
- if (IS_ERR(phy->clk)) {
- pr_err("%s: can't get ulpi clock\n", __func__);
- err = -ENXIO;
- goto err1;
+
+ if (ulpi_config->inf_type == TEGRA_USB_LINK_ULPI) {
+ phy->clk = clk_get_sys(NULL, ulpi_config->clk);
+ if (IS_ERR(phy->clk)) {
+ pr_err("%s: can't get ulpi clock\n", __func__);
+ err = -ENXIO;
+ goto err1;
+ }
+ 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);
}
- 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);
} else {
err = utmip_pad_open(phy);
if (err < 0)
@@ -728,9 +852,14 @@ err0:
int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
{
- if (phy->instance == 1)
- ulpi_phy_power_on(phy);
- else
+ if (phy->instance == 1) {
+ struct tegra_ulpi_config *ulpi_config = phy->config;
+
+ if (ulpi_config->inf_type == TEGRA_USB_LINK_ULPI)
+ ulpi_phy_power_on(phy);
+ else
+ null_phy_power_on(phy);
+ } else
utmi_phy_power_on(phy);
return 0;
@@ -738,9 +867,14 @@ int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
{
- if (phy->instance == 1)
- ulpi_phy_power_off(phy);
- else
+ if (phy->instance == 1) {
+ struct tegra_ulpi_config *ulpi_config = phy->config;
+
+ if (ulpi_config->inf_type == TEGRA_USB_LINK_ULPI)
+ ulpi_phy_power_off(phy);
+ else
+ null_phy_power_off(phy);
+ } else
utmi_phy_power_off(phy);
return 0;
@@ -792,9 +926,12 @@ int tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy)
int tegra_usb_phy_close(struct tegra_usb_phy *phy)
{
- if (phy->instance == 1)
- clk_put(phy->clk);
- else
+ if (phy->instance == 1) {
+ struct tegra_ulpi_config *ulpi_config = phy->config;
+
+ if (ulpi_config->inf_type == TEGRA_USB_LINK_ULPI)
+ clk_put(phy->clk);
+ } else
utmip_pad_close(phy);
clk_disable(phy->pll_u);
clk_put(phy->pll_u);