summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/board-whistler-baseband.c60
-rw-r--r--arch/arm/mach-tegra/tegra2_usb_phy.c192
2 files changed, 243 insertions, 9 deletions
diff --git a/arch/arm/mach-tegra/board-whistler-baseband.c b/arch/arm/mach-tegra/board-whistler-baseband.c
index eb50fb2f8237..b602cd3f44c1 100644
--- a/arch/arm/mach-tegra/board-whistler-baseband.c
+++ b/arch/arm/mach-tegra/board-whistler-baseband.c
@@ -26,8 +26,9 @@
static void baseband_phy_init(void);
static void baseband_phy_on(void);
-static void baseband_phy_off(void);
-
+static void baseband_pre_phy_off(void);
+static void baseband_post_phy_off(void);
+static bool ap2mdm_ack_gpio_off = false;
static struct wake_lock mdm_wake_lock;
static struct gpio modem_gpios[] = {
@@ -58,13 +59,14 @@ static __initdata struct tegra_pingroup_config whistler_null_ulpi_pinmux[] = {
static struct tegra_usb_phy_platform_ops ulpi_null_plat_ops = {
.init = baseband_phy_init,
- .pre_phy_off = baseband_phy_off,
+ .pre_phy_off = baseband_pre_phy_off,
+ .post_phy_off = baseband_post_phy_off,
.post_phy_on = baseband_phy_on,
};
static struct tegra_usb_platform_data tegra_ehci2_ulpi_null_pdata = {
.port_otg = false,
- .has_hostpc = true,
+ .has_hostpc = false,
.phy_intf = TEGRA_USB_PHY_INTF_ULPI_NULL,
.op_mode = TEGRA_USB_OPMODE_HOST,
.u_data.host = {
@@ -105,6 +107,15 @@ static irqreturn_t mdm_start_thread(int irq, void *data)
return IRQ_HANDLED;
}
+static inline void null_phy_set_tristate(bool enable)
+{
+ int tristate = (enable) ? TEGRA_TRI_TRISTATE : TEGRA_TRI_NORMAL;
+
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_UDA, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAA, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAB, tristate);
+}
+
static void baseband_phy_init(void)
{
static bool phy_init;
@@ -117,18 +128,51 @@ static void baseband_phy_init(void)
pr_info("%s\n", __func__);
}
-static void baseband_phy_off(void)
+static void baseband_pre_phy_off(void)
{
/* set AP2MDM_ACK2 high */
gpio_set_value(AP2MDM_ACK2, 1);
+ ap2mdm_ack_gpio_off = true;
+}
+
+static void baseband_post_phy_off(void)
+{
+ null_phy_set_tristate(true);
}
-static void baseband_phy_on (void)
+static void baseband_phy_on(void)
{
- /* set AP2MDM_ACK2 low */
- gpio_set_value(AP2MDM_ACK2, 0);
+ if (ap2mdm_ack_gpio_off) {
+
+ /* driving linestate using GPIO */
+ gpio_set_value(ULPI_D0, 0);
+ gpio_set_value(ULPI_D1, 0);
+
+ /* driving DIR high */
+ gpio_set_value(ULPI_DIR, 1);
+
+ /* remove ULPI tristate */
+ null_phy_set_tristate(false);
+
+ gpio_set_value(AP2MDM_ACK2, 0);
+
+ if (gpio_is_valid(MDM2AP_ACK2)) {
+ int retry = 20000;
+ while (retry) {
+ /* poll phy_restore_gpio high */
+ if (gpio_get_value(MDM2AP_ACK2))
+ break;
+ retry--;
+ }
+
+ if (retry == 0)
+ pr_info("phy_restore_gpio timeout\n");
+ }
+ ap2mdm_ack_gpio_off = false;
+ }
}
+
static void baseband_start(void)
{
/*
diff --git a/arch/arm/mach-tegra/tegra2_usb_phy.c b/arch/arm/mach-tegra/tegra2_usb_phy.c
index 819721c49a29..7b18333cc77c 100644
--- a/arch/arm/mach-tegra/tegra2_usb_phy.c
+++ b/arch/arm/mach-tegra/tegra2_usb_phy.c
@@ -1545,6 +1545,189 @@ static int ulpi_link_phy_resume(struct tegra_usb_phy *phy)
return status;
}
+
+static int ulpi_null_phy_power_off(struct tegra_usb_phy *phy)
+{
+ DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
+
+ if (!phy->phy_clk_on) {
+ DBG("%s(%d) inst:[%d] phy clk is already off\n", __func__,
+ __LINE__, phy->inst);
+ return 0;
+ }
+
+ phy->phy_clk_on = false;
+ phy->hw_accessible = false;
+
+ return 0;
+}
+
+static int ulpi_null_phy_irq(struct tegra_usb_phy *phy)
+{
+ usb_phy_fence_read(phy);
+ return IRQ_HANDLED;
+}
+
+static int ulpi_null_phy_lp0_resume(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ val = readl(base + USB_USBCMD);
+ val |= USB_USBCMD_RESET;
+ writel(val, base + USB_USBCMD);
+
+ if (usb_phy_reg_status_wait(base + USB_USBCMD,
+ USB_USBCMD_RESET, 0, 2500) < 0) {
+ pr_err("%s: timeout waiting for reset\n", __func__);
+ }
+
+ val = readl(base + USB_USBMODE_REG_OFFSET);
+ val &= ~USB_USBMODE_MASK;
+ val |= USB_USBMODE_HOST;
+ writel(val, base + USB_USBMODE_REG_OFFSET);
+
+ val = readl(base + USB_USBCMD);
+ val |= USB_USBCMD_RS;
+ writel(val, base + USB_USBCMD);
+ if (usb_phy_reg_status_wait(base + USB_USBCMD, USB_USBCMD_RS,
+ USB_USBCMD_RS, 2000)) {
+ pr_err("%s: timeout waiting for USB_USBCMD_RS\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ /* Enable Port Power */
+ val = readl(base + USB_PORTSC);
+ val |= USB_PORTSC_PP;
+ writel(val, base + USB_PORTSC);
+ udelay(10);
+
+ /* disable ULPI pinmux bypass */
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val &= ~ULPI_OUTPUT_PINMUX_BYP;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+
+ return 0;
+}
+
+static int ulpi_null_phy_power_on(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ struct tegra_ulpi_config *config = &phy->pdata->u_cfg.ulpi;
+ static bool cold_boot = true;
+
+ DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
+ if (phy->phy_clk_on) {
+ DBG("%s(%d) inst:[%d] phy clk is already On\n", __func__,
+ __LINE__, phy->inst);
+ return 0;
+ }
+
+ 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);
+ udelay(10);
+
+ /* set timming parameters */
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_SHADOW_CLK_LOOPBACK_EN;
+ val |= ULPI_SHADOW_CLK_SEL;
+ val |= ULPI_LBK_PAD_EN;
+ val |= ULPI_SHADOW_CLK_DELAY(config->shadow_clk_delay);
+ val |= ULPI_CLOCK_OUT_DELAY(config->clock_out_delay);
+ val |= ULPI_LBK_PAD_E_INPUT_OR;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+
+ writel(0, base + ULPI_TIMING_CTRL_1);
+ udelay(10);
+
+ /* start internal 60MHz clock */
+ val = readl(base + ULPIS2S_CTRL);
+ val |= ULPIS2S_ENA;
+ val |= ULPIS2S_SUPPORT_DISCONNECT;
+ val |= ULPIS2S_SPARE((phy->pdata->op_mode == TEGRA_USB_OPMODE_HOST) ? 3 : 1);
+ val |= ULPIS2S_PLLU_MASTER_BLASTER60;
+ 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 phy clock - 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);
+
+ if (usb_phy_reg_status_wait(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+ USB_PHY_CLK_VALID, 2500)) {
+ pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ /* set ULPI trimmers */
+ ulpi_set_trimmer(phy);
+
+ if (cold_boot) {
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_CLK_PADOUT_ENA;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+ cold_boot = false;
+ } else {
+ if (!readl(base + USB_ASYNCLISTADDR))
+ ulpi_null_phy_lp0_resume(phy);
+ }
+ udelay(10);
+
+ phy->phy_clk_on = true;
+ phy->hw_accessible = true;
+
+ return 0;
+}
+
+
+static int ulpi_null_phy_pre_resume(struct tegra_usb_phy *phy, bool remote_wakeup)
+{
+ DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
+
+ usb_phy_wait_for_sof(phy);
+ return 0;
+}
+
+static int ulpi_null_phy_resume(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ if (!readl(base + USB_ASYNCLISTADDR)) {
+ /* enable ULPI CLK output pad */
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_CLK_PADOUT_ENA;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+
+ /* enable ULPI pinmux bypass */
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_OUTPUT_PINMUX_BYP;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+ udelay(5);
+ }
+
+ return 0;
+}
+
+
+
static struct tegra_usb_phy_ops utmi_phy_ops = {
.open = utmi_phy_open,
.close = utmi_phy_close,
@@ -1578,7 +1761,14 @@ static struct tegra_usb_phy_ops ulpi_link_phy_ops = {
.resume = ulpi_link_phy_resume,
};
-static struct tegra_usb_phy_ops ulpi_null_phy_ops;
+static struct tegra_usb_phy_ops ulpi_null_phy_ops = {
+ .irq = ulpi_null_phy_irq,
+ .power_on = ulpi_null_phy_power_on,
+ .power_off = ulpi_null_phy_power_off,
+ .pre_resume = ulpi_null_phy_pre_resume,
+ .resume = ulpi_null_phy_resume,
+};
+
static struct tegra_usb_phy_ops icusb_phy_ops;