blob: 19540aa7e1ea01798bfe04173af1738e7c1ecfcf [file] [log] [blame]
/*
* Copyright (C) 2013 NVIDIA Corporation. All rights reserved.
*
* 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/export.h>
#include <linux/resource.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/usb/ulpi.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_data/tegra_usb.h>
#include <linux/clk/tegra.h>
#include <mach/pinmux.h>
#include <mach/pinmux-t11.h>
#include <mach/tegra_usb_pmc.h>
#include "../../../arch/arm/mach-tegra/iomap.h"
static void __iomem *pmc_base;
static unsigned long flags;
static DEFINE_SPINLOCK(pmc_lock);
static u32 utmip_rctrl_val;
static u32 utmip_tctrl_val;
#ifdef KERNEL_WARNING
static void usb_phy_power_down_pmc(struct tegra_usb_pmc_data *pmc_data)
{
unsigned long val;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, pmc_data->instance);
spin_lock_irqsave(&pmc_lock, flags);
/* power down all 3 UTMIP interfaces */
val = readl(pmc_base + PMC_UTMIP_MASTER_CONFIG);
val |= UTMIP_PWR(0) | UTMIP_PWR(1) | UTMIP_PWR(2);
writel(val, pmc_base + PMC_UTMIP_MASTER_CONFIG);
/* turn on pad detectors */
writel(PMC_POWER_DOWN_MASK, pmc_base + PMC_USB_AO);
/* setup sleep walk fl all 3 usb controllers */
val = UTMIP_USBOP_RPD_A | UTMIP_USBON_RPD_A | UTMIP_HIGHZ_A |
UTMIP_USBOP_RPD_B | UTMIP_USBON_RPD_B | UTMIP_HIGHZ_B |
UTMIP_USBOP_RPD_C | UTMIP_USBON_RPD_C | UTMIP_HIGHZ_C |
UTMIP_USBOP_RPD_D | UTMIP_USBON_RPD_D | UTMIP_HIGHZ_D;
writel(val, pmc_base + PMC_SLEEPWALK_REG(0));
writel(val, pmc_base + PMC_SLEEPWALK_REG(1));
writel(val, pmc_base + PMC_SLEEPWALK_REG(2));
/* enable pull downs on HSIC PMC */
val = UHSIC_STROBE_RPD_A | UHSIC_DATA_RPD_A | UHSIC_STROBE_RPD_B |
UHSIC_DATA_RPD_B | UHSIC_STROBE_RPD_C | UHSIC_DATA_RPD_C |
UHSIC_STROBE_RPD_D | UHSIC_DATA_RPD_D;
writel(val, pmc_base + PMC_SLEEPWALK_UHSIC);
/* Turn over pad configuration to PMC */
val = readl(pmc_base + PMC_SLEEP_CFG);
val &= ~UTMIP_WAKE_VAL(0, ~0);
val &= ~UTMIP_WAKE_VAL(1, ~0);
val &= ~UTMIP_WAKE_VAL(2, ~0);
val &= ~UHSIC_WAKE_VAL_P0(~0);
val |= UTMIP_WAKE_VAL(0, WAKE_VAL_NONE) |
UHSIC_WAKE_VAL_P0(WAKE_VAL_NONE) |
UTMIP_WAKE_VAL(1, WAKE_VAL_NONE) | UTMIP_WAKE_VAL(2, WAKE_VAL_NONE) |
UTMIP_RCTRL_USE_PMC(0) | UTMIP_RCTRL_USE_PMC(1) |
UTMIP_RCTRL_USE_PMC(2) |
UTMIP_TCTRL_USE_PMC(0) | UTMIP_TCTRL_USE_PMC(1) |
UTMIP_TCTRL_USE_PMC(2) |
UTMIP_FSLS_USE_PMC(0) | UTMIP_FSLS_USE_PMC(1) |
UTMIP_FSLS_USE_PMC(2) |
UTMIP_MASTER_ENABLE(0) | UTMIP_MASTER_ENABLE(1) |
UTMIP_MASTER_ENABLE(2) |
UHSIC_MASTER_ENABLE_P0;
writel(val, pmc_base + PMC_SLEEP_CFG);
spin_unlock_irqrestore(&pmc_lock, flags);
}
#endif
void utmi_phy_update_trking_data(u32 tctrl, u32 rctrl)
{
spin_lock_irqsave(&pmc_lock, flags);
utmip_tctrl_val = tctrl;
utmip_rctrl_val = rctrl;
spin_unlock_irqrestore(&pmc_lock, flags);
}
EXPORT_SYMBOL_GPL(utmi_phy_update_trking_data);
int utmi_phy_set_snps_trking_data(void)
{
void __iomem *base = IO_ADDRESS(TEGRA_USB_BASE);
u32 val;
struct clk *utmi_pad_clk;
utmi_pad_clk = clk_get_sys("utmip-pad", NULL);
if (IS_ERR(utmi_pad_clk)) {
pr_err("%s: can't get utmip pad clock\n", __func__);
return PTR_ERR(utmi_pad_clk);
}
if (!pmc_base)
pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
clk_enable(utmi_pad_clk);
spin_lock_irqsave(&pmc_lock, flags);
/* Bias pad MASTER_ENABLE=1 */
val = readl(pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL);
val |= BIAS_MASTER_PROG_VAL;
writel(val, pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL);
/* Setting the tracking length time */
val = readl(base + UTMIP_BIAS_CFG1);
val &= ~UTMIP_BIAS_PDTRK_COUNT(~0);
val |= UTMIP_BIAS_PDTRK_COUNT(5);
writel(val, base + UTMIP_BIAS_CFG1);
/* Bias PDTRK is Shared and MUST be done from USB1 ONLY, PD_TRK=0 */
val = readl(base + UTMIP_BIAS_CFG1);
val &= ~UTMIP_BIAS_PDTRK_POWERDOWN;
writel(val, base + UTMIP_BIAS_CFG1);
val = readl(base + UTMIP_BIAS_CFG1);
val |= UTMIP_BIAS_PDTRK_POWERUP;
writel(val, base + UTMIP_BIAS_CFG1);
/* Wait for 25usec */
udelay(25);
/* Bias pad MASTER_ENABLE=0 */
val = readl(pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL);
val &= ~BIAS_MASTER_PROG_VAL;
writel(val, pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL);
/* Wait for 1usec */
udelay(1);
/* Bias pad MASTER_ENABLE=1 */
val = readl(pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL);
val |= BIAS_MASTER_PROG_VAL;
writel(val, pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL);
/* Read RCTRL and TCTRL from UTMIP space */
val = readl(base + UTMIP_BIAS_STS0);
utmip_rctrl_val = 0xf + ffz(UTMIP_RCTRL_VAL(val));
utmip_tctrl_val = 0xf + ffz(UTMIP_TCTRL_VAL(val));
/* PD_TRK=1 */
val = readl(base + UTMIP_BIAS_CFG1);
val |= UTMIP_BIAS_PDTRK_POWERDOWN;
writel(val, base + UTMIP_BIAS_CFG1);
/* Program thermally encoded RCTRL_VAL, TCTRL_VAL into PMC space */
val = readl(pmc_base + PMC_UTMIP_TERM_PAD_CFG);
val = PMC_TCTRL_VAL(utmip_tctrl_val) |
PMC_RCTRL_VAL(utmip_rctrl_val);
writel(val, pmc_base + PMC_UTMIP_TERM_PAD_CFG);
spin_unlock_irqrestore(&pmc_lock, flags);
clk_disable(utmi_pad_clk);
clk_put(utmi_pad_clk);
return 0;
}
EXPORT_SYMBOL_GPL(utmi_phy_set_snps_trking_data);
static void utmip_setup_pmc_wake_detect(struct tegra_usb_pmc_data *pmc_data)
{
unsigned long val, pmc_pad_cfg_val;
unsigned int inst = pmc_data->instance;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, pmc_data->instance);
spin_lock_irqsave(&pmc_lock, flags);
val = readl(pmc_base + PMC_SLEEP_CFG);
if (val & UTMIP_MASTER_ENABLE(inst)) {
DBG("%s(%d) inst:[%d] pmc already enabled\n",
__func__, __LINE__, pmc_data->instance);
spin_unlock_irqrestore(&pmc_lock, flags);
return;
}
/*Set PMC MASTER bits to do the following
* a. Take over the UTMI drivers
* b. set up such that it will take over resume
* if remote wakeup is detected
* Prepare PMC to take over suspend-wake detect-drive resume until USB
* controller ready
*/
/* disable master enable in PMC */
val = readl(pmc_base + PMC_SLEEP_CFG);
val &= ~UTMIP_MASTER_ENABLE(inst);
writel(val, pmc_base + PMC_SLEEP_CFG);
/* UTMIP_PWR_PX=1 for power savings mode */
val = readl(pmc_base + PMC_UTMIP_MASTER_CONFIG);
val |= UTMIP_PWR(inst);
writel(val, pmc_base + PMC_UTMIP_MASTER_CONFIG);
/* config debouncer */
val = readl(pmc_base + PMC_USB_DEBOUNCE);
val &= ~UTMIP_LINE_DEB_CNT(~0);
val |= UTMIP_LINE_DEB_CNT(1);
val |= PMC_USB_DEBOUNCE_VAL(2);
writel(val, pmc_base + PMC_USB_DEBOUNCE);
/* Make sure nothing is happening on the line with respect to PMC */
val = readl(pmc_base + PMC_UTMIP_FAKE);
val &= ~USBOP_VAL(inst);
val &= ~USBON_VAL(inst);
writel(val, pmc_base + PMC_UTMIP_FAKE);
/* Make sure wake value for line is none */
val = readl(pmc_base + PMC_SLEEPWALK_CFG);
val &= ~UTMIP_LINEVAL_WALK_EN(inst);
writel(val, pmc_base + PMC_SLEEPWALK_CFG);
val = readl(pmc_base + PMC_SLEEP_CFG);
val &= ~UTMIP_WAKE_VAL(inst, ~0);
val |= UTMIP_WAKE_VAL(inst, WAKE_VAL_NONE);
writel(val, pmc_base + PMC_SLEEP_CFG);
/* turn off pad detectors */
val = readl(pmc_base + PMC_USB_AO);
val |= (USBOP_VAL_PD(inst) | USBON_VAL_PD(inst));
writel(val, pmc_base + PMC_USB_AO);
/* Remove fake values and make synchronizers work a bit */
val = readl(pmc_base + PMC_UTMIP_FAKE);
val &= ~USBOP_VAL(inst);
val &= ~USBON_VAL(inst);
writel(val, pmc_base + PMC_UTMIP_FAKE);
/* Enable which type of event can trigger a walk,
* in this case usb_line_wake */
val = readl(pmc_base + PMC_SLEEPWALK_CFG);
val |= UTMIP_LINEVAL_WALK_EN(inst);
writel(val, pmc_base + PMC_SLEEPWALK_CFG);
/* Capture FS/LS pad configurations */
pmc_pad_cfg_val = readl(pmc_base + PMC_PAD_CFG);
val = readl(pmc_base + PMC_TRIGGERS);
val |= UTMIP_CAP_CFG(inst);
writel(val, pmc_base + PMC_TRIGGERS);
udelay(1);
pmc_pad_cfg_val = readl(pmc_base + PMC_PAD_CFG);
/* BIAS MASTER_ENABLE=0 */
val = readl(pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL);
val &= ~BIAS_MASTER_PROG_VAL;
writel(val, pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL);
/* program walk sequence for remote or hotplug wakeup */
if ((pmc_data->port_speed < USB_PMC_PORT_SPEED_UNKNOWN) ||
(pmc_data->port_speed == USB_PMC_PORT_SPEED_SUPER)) {
/* program walk sequence, maintain a J, followed by a driven K
* to signal a resume once an wake event is detected */
val = readl(pmc_base + PMC_SLEEPWALK_REG(inst));
val &= ~UTMIP_AP_A;
val |= UTMIP_USBOP_RPD_A | UTMIP_USBON_RPD_A | UTMIP_AN_A |
UTMIP_HIGHZ_A |
UTMIP_USBOP_RPD_B | UTMIP_USBON_RPD_B | UTMIP_AP_B |
UTMIP_AN_B |
UTMIP_USBOP_RPD_C | UTMIP_USBON_RPD_C | UTMIP_AP_C |
UTMIP_AN_C |
UTMIP_USBOP_RPD_D | UTMIP_USBON_RPD_D | UTMIP_AP_D |
UTMIP_AN_D;
writel(val, pmc_base + PMC_SLEEPWALK_REG(inst));
if (pmc_data->port_speed == USB_PMC_PORT_SPEED_LOW) {
val = readl(pmc_base + PMC_SLEEPWALK_REG(inst));
val &= ~(UTMIP_AN_B | UTMIP_HIGHZ_B | UTMIP_AN_C |
UTMIP_HIGHZ_C | UTMIP_AN_D | UTMIP_HIGHZ_D);
writel(val, pmc_base + PMC_SLEEPWALK_REG(inst));
} else {
val = readl(pmc_base + PMC_SLEEPWALK_REG(inst));
val &= ~(UTMIP_AP_B | UTMIP_HIGHZ_B | UTMIP_AP_C |
UTMIP_HIGHZ_C | UTMIP_AP_D | UTMIP_HIGHZ_D |
UTMIP_AN_A);
val |= UTMIP_AP_A;
writel(val, pmc_base + PMC_SLEEPWALK_REG(inst));
}
} else {
/* program walk sequence, pull down both dp and dn lines,
* tristate lines once an hotplug-in wake event is detected */
val = readl(pmc_base + PMC_SLEEPWALK_REG(inst));
val |= UTMIP_USBOP_RPD_A | UTMIP_USBON_RPD_A | UTMIP_HIGHZ_A;
val &= ~UTMIP_AP_A;
val &= ~UTMIP_AN_A;
val |= UTMIP_USBOP_RPD_B | UTMIP_USBON_RPD_B | UTMIP_HIGHZ_B;
val &= ~UTMIP_AP_B;
val &= ~UTMIP_AN_B;
val |= UTMIP_USBOP_RPD_C | UTMIP_USBON_RPD_C | UTMIP_HIGHZ_C;
val &= ~UTMIP_AP_C;
val &= ~UTMIP_AN_C;
val |= UTMIP_USBOP_RPD_D | UTMIP_USBON_RPD_D | UTMIP_HIGHZ_D;
val &= ~UTMIP_AP_D;
val &= ~UTMIP_AN_D;
writel(val, pmc_base + PMC_SLEEPWALK_REG(inst));
}
/* turn on pad detectors */
val = readl(pmc_base + PMC_USB_AO);
val &= ~(USBOP_VAL_PD(inst) | USBON_VAL_PD(inst));
writel(val, pmc_base + PMC_USB_AO);
spin_unlock_irqrestore(&pmc_lock, flags);
/* Add small delay before usb detectors provide stable line values */
mdelay(5);
spin_lock_irqsave(&pmc_lock, flags);
/* Program thermally encoded RCTRL_VAL, TCTRL_VAL into PMC space */
if (utmip_tctrl_val | utmip_rctrl_val) {
val = readl(pmc_base + PMC_UTMIP_TERM_PAD_CFG);
val = PMC_TCTRL_VAL(utmip_tctrl_val) |
PMC_RCTRL_VAL(utmip_rctrl_val);
writel(val, pmc_base + PMC_UTMIP_TERM_PAD_CFG);
}
/* Turn over pad configuration to PMC for line wake events*/
val = readl(pmc_base + PMC_SLEEP_CFG);
val &= ~UTMIP_WAKE_VAL(inst, ~0);
val |= UTMIP_WAKE_VAL(inst, WAKE_VAL_ANY);
val |= UTMIP_RCTRL_USE_PMC(inst) | UTMIP_TCTRL_USE_PMC(inst);
val |= UTMIP_MASTER_ENABLE(inst) | UTMIP_FSLS_USE_PMC(inst);
writel(val, pmc_base + PMC_SLEEP_CFG);
spin_unlock_irqrestore(&pmc_lock, flags);
}
static void utmip_phy_disable_pmc_bus_ctrl(struct tegra_usb_pmc_data *pmc_data,
int enable_sof)
{
unsigned long val;
void __iomem *usb_base;
unsigned int inst = pmc_data->instance;
usb_base = pmc_data->usb_base;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, pmc_data->instance);
spin_lock_irqsave(&pmc_lock, flags);
val = readl(pmc_base + PMC_SLEEP_CFG);
if (!(val & UTMIP_MASTER_ENABLE(inst))) {
DBG("%s(%d) inst:[%d] pmc already disabled\n",
__func__, __LINE__, pmc_data->instance);
spin_unlock_irqrestore(&pmc_lock, flags);
return;
}
if (pmc_data->controller_type == TEGRA_USB_2_0 && usb_base) {
/* disable PMC master control */
val = readl(usb_base + UTMIP_PMC_WAKEUP0);
val &= ~EVENT_INT_ENB;
writel(val, usb_base + UTMIP_PMC_WAKEUP0);
}
val = readl(pmc_base + PMC_SLEEP_CFG);
val &= ~UTMIP_WAKE_VAL(inst, 0xF);
val |= UTMIP_WAKE_VAL(inst, WAKE_VAL_NONE);
writel(val, pmc_base + PMC_SLEEP_CFG);
/* Disable PMC master mode by clearing MASTER_EN */
val = readl(pmc_base + PMC_SLEEP_CFG);
/* WAR for xusb */
if (pmc_data->controller_type == TEGRA_USB_3_0)
val |= UTMIP_RCTRL_USE_PMC(inst) | UTMIP_TCTRL_USE_PMC(inst);
else
val &= ~(UTMIP_RCTRL_USE_PMC(inst) |
UTMIP_TCTRL_USE_PMC(inst));
val &= ~(UTMIP_FSLS_USE_PMC(inst) | UTMIP_MASTER_ENABLE(inst));
writel(val, pmc_base + PMC_SLEEP_CFG);
val = readl(pmc_base + PMC_TRIGGERS);
val &= ~UTMIP_CAP_CFG(inst);
writel(val, pmc_base + PMC_TRIGGERS);
/* turn off pad detectors */
val = readl(pmc_base + PMC_USB_AO);
val |= (USBOP_VAL_PD(inst) | USBON_VAL_PD(inst));
writel(val, pmc_base + PMC_USB_AO);
val = readl(pmc_base + PMC_TRIGGERS);
val |= UTMIP_CLR_WALK_PTR(inst);
val |= UTMIP_CLR_WAKE_ALARM(inst);
writel(val, pmc_base + PMC_TRIGGERS);
if (pmc_data->controller_type == TEGRA_USB_2_0 && enable_sof == 1 &&
usb_base) {
val = readl(usb_base + USB_USBCMD);
val |= USB_USBCMD_RS;
writel(val, usb_base + USB_USBCMD);
}
spin_unlock_irqrestore(&pmc_lock, flags);
}
static void utmip_powerdown_pmc_wake_detect(struct tegra_usb_pmc_data *pmc_data)
{
unsigned long val;
unsigned int inst = pmc_data->instance;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, pmc_data->instance);
if (pmc_data->is_xhci)
return;
spin_lock_irqsave(&pmc_lock, flags);
/* power down UTMIP interfaces */
val = readl(pmc_base + PMC_UTMIP_MASTER_CONFIG);
val |= UTMIP_PWR(inst);
writel(val, pmc_base + PMC_UTMIP_MASTER_CONFIG);
/* setup sleep walk usb controller */
val = UTMIP_USBOP_RPD_A | UTMIP_USBON_RPD_A | UTMIP_HIGHZ_A |
UTMIP_USBOP_RPD_B | UTMIP_USBON_RPD_B | UTMIP_HIGHZ_B |
UTMIP_USBOP_RPD_C | UTMIP_USBON_RPD_C | UTMIP_HIGHZ_C |
UTMIP_USBOP_RPD_D | UTMIP_USBON_RPD_D | UTMIP_HIGHZ_D;
writel(val, pmc_base + PMC_SLEEPWALK_REG(inst));
/* Program thermally encoded RCTRL_VAL, TCTRL_VAL into PMC space */
if (utmip_tctrl_val | utmip_rctrl_val) {
val = readl(pmc_base + PMC_UTMIP_TERM_PAD_CFG);
val = PMC_TCTRL_VAL(utmip_tctrl_val) |
PMC_RCTRL_VAL(utmip_rctrl_val);
writel(val, pmc_base + PMC_UTMIP_TERM_PAD_CFG);
}
/* Turn over pad configuration to PMC */
val = readl(pmc_base + PMC_SLEEP_CFG);
val &= ~UTMIP_WAKE_VAL(inst, ~0);
val |= UTMIP_WAKE_VAL(inst, WAKE_VAL_NONE) |
UTMIP_RCTRL_USE_PMC(inst) | UTMIP_TCTRL_USE_PMC(inst) |
UTMIP_FSLS_USE_PMC(inst) | UTMIP_MASTER_ENABLE(inst);
writel(val, pmc_base + PMC_SLEEP_CFG);
spin_unlock_irqrestore(&pmc_lock, flags);
}
static void utmip_powerup_pmc_wake_detect(struct tegra_usb_pmc_data *pmc_data)
{
unsigned long val;
unsigned int inst = pmc_data->instance;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, pmc_data->instance);
if (pmc_data->is_xhci)
return;
spin_lock_irqsave(&pmc_lock, flags);
/* Disable PMC master mode by clearing MASTER_EN */
val = readl(pmc_base + PMC_SLEEP_CFG);
/* WAR for xusb */
if (pmc_data->controller_type == TEGRA_USB_3_0)
val |= UTMIP_RCTRL_USE_PMC(inst) | UTMIP_TCTRL_USE_PMC(inst);
else
val &= ~(UTMIP_RCTRL_USE_PMC(inst) |
UTMIP_TCTRL_USE_PMC(inst));
val &= ~(UTMIP_FSLS_USE_PMC(inst) | UTMIP_MASTER_ENABLE(inst));
writel(val, pmc_base + PMC_SLEEP_CFG);
spin_unlock_irqrestore(&pmc_lock, flags);
mdelay(1);
}
static void uhsic_powerup_pmc_wake_detect(struct tegra_usb_pmc_data *pmc_data)
{
unsigned long val;
unsigned int inst = pmc_data->instance;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, pmc_data->instance);
spin_lock_irqsave(&pmc_lock, flags);
/* turn on pad detectors for HSIC*/
val = readl(pmc_base + PMC_USB_AO);
val &= ~(HSIC_RESERVED(inst) | STROBE_VAL_PD(inst) | DATA_VAL_PD(inst));
writel(val, pmc_base + PMC_USB_AO);
/* Disable PMC master mode by clearing MASTER_EN */
val = readl(pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
val &= ~(UHSIC_MASTER_ENABLE(inst));
writel(val, pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
spin_unlock_irqrestore(&pmc_lock, flags);
mdelay(1);
}
static void uhsic_powerdown_pmc_wake_detect(struct tegra_usb_pmc_data *pmc_data)
{
unsigned long val;
unsigned int inst = pmc_data->instance;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, pmc_data->instance);
spin_lock_irqsave(&pmc_lock, flags);
/* turn off pad detectors for HSIC*/
val = readl(pmc_base + PMC_USB_AO);
val |= (HSIC_RESERVED(inst) | STROBE_VAL_PD(inst) | DATA_VAL_PD(inst));
writel(val, pmc_base + PMC_USB_AO);
/* enable pull downs on HSIC PMC */
val = UHSIC_STROBE_RPD_A | UHSIC_DATA_RPD_A | UHSIC_STROBE_RPD_B |
UHSIC_DATA_RPD_B | UHSIC_STROBE_RPD_C | UHSIC_DATA_RPD_C |
UHSIC_STROBE_RPD_D | UHSIC_DATA_RPD_D;
writel(val, pmc_base + PMC_SLEEPWALK_UHSIC(inst));
/* Turn over pad configuration to PMC */
val = readl(pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
val &= ~UHSIC_WAKE_VAL(inst, ~0);
val |= UHSIC_WAKE_VAL(inst, WAKE_VAL_NONE) | UHSIC_MASTER_ENABLE(inst);
writel(val, pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
spin_unlock_irqrestore(&pmc_lock, flags);
}
static void uhsic_setup_pmc_wake_detect(struct tegra_usb_pmc_data *pmc_data)
{
unsigned long val;
unsigned int inst = pmc_data->instance;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, pmc_data->instance);
spin_lock_irqsave(&pmc_lock, flags);
val = readl(pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
if (val & UHSIC_MASTER_ENABLE(inst)) {
DBG("%s(%d) inst:[%d] pmc already enabled\n",
__func__, __LINE__, pmc_data->instance);
spin_unlock_irqrestore(&pmc_lock, flags);
return;
}
/*Set PMC MASTER bits to do the following
* a. Take over the hsic drivers
* b. set up such that it will take over resume
* if remote wakeup is detected
* Prepare PMC to take over suspend-wake detect-drive resume until USB
* controller ready
*/
/* disable master enable in PMC */
val = readl(pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
val &= ~UHSIC_MASTER_ENABLE(inst);
writel(val, pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
/* UTMIP_PWR_PX=1 for power savings mode */
val = readl(pmc_base + PMC_UHSIC_MASTER_CONFIG(inst));
val |= UHSIC_PWR(inst);
writel(val, pmc_base + PMC_UHSIC_MASTER_CONFIG(inst));
/* config debouncer */
val = readl(pmc_base + PMC_USB_DEBOUNCE);
val |= PMC_USB_DEBOUNCE_VAL(2);
writel(val, pmc_base + PMC_USB_DEBOUNCE);
/* Make sure nothing is happening on the line with respect to PMC */
val = readl(pmc_base + PMC_UHSIC_FAKE(inst));
val &= ~UHSIC_FAKE_STROBE_VAL(inst);
val &= ~UHSIC_FAKE_DATA_VAL(inst);
writel(val, pmc_base + PMC_UHSIC_FAKE(inst));
/* Clear walk enable */
val = readl(pmc_base + PMC_UHSIC_SLEEPWALK_CFG(inst));
val &= ~UHSIC_LINEVAL_WALK_EN(inst);
writel(val, pmc_base + PMC_UHSIC_SLEEPWALK_CFG(inst));
/* Make sure wake value for line is none */
val = readl(pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
val &= ~UHSIC_WAKE_VAL(inst, WAKE_VAL_ANY);
val |= UHSIC_WAKE_VAL(inst, WAKE_VAL_NONE);
writel(val, pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
/* turn on pad detectors */
val = readl(pmc_base + PMC_USB_AO);
val &= ~(STROBE_VAL_PD(inst) | DATA_VAL_PD(inst));
writel(val, pmc_base + PMC_USB_AO);
/* Add small delay before usb detectors provide stable line values */
udelay(1);
/* Enable which type of event can trigger a walk,
* in this case usb_line_wake */
val = readl(pmc_base + PMC_UHSIC_SLEEPWALK_CFG(inst));
val |= UHSIC_LINEVAL_WALK_EN(inst);
writel(val, pmc_base + PMC_UHSIC_SLEEPWALK_CFG(inst));
/* program walk sequence, maintain a J, followed by a driven K
* to signal a resume once an wake event is detected */
val = readl(pmc_base + PMC_SLEEPWALK_UHSIC(inst));
val &= ~UHSIC_DATA_RPU_A;
val |= UHSIC_DATA_RPD_A;
val &= ~UHSIC_STROBE_RPD_A;
val |= UHSIC_STROBE_RPU_A;
val &= ~UHSIC_DATA_RPD_B;
val |= UHSIC_DATA_RPU_B;
val &= ~UHSIC_STROBE_RPU_B;
val |= UHSIC_STROBE_RPD_B;
val &= ~UHSIC_DATA_RPD_C;
val |= UHSIC_DATA_RPU_C;
val &= ~UHSIC_STROBE_RPU_C;
val |= UHSIC_STROBE_RPD_C;
val &= ~UHSIC_DATA_RPD_D;
val |= UHSIC_DATA_RPU_D;
val &= ~UHSIC_STROBE_RPU_D;
val |= UHSIC_STROBE_RPD_D;
writel(val, pmc_base + PMC_SLEEPWALK_UHSIC(inst));
/* Setting Wake event*/
val = readl(pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
val &= ~UHSIC_WAKE_VAL(inst, WAKE_VAL_ANY);
val |= UHSIC_WAKE_VAL(inst, WAKE_VAL_SD10);
writel(val, pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
/* Clear the walk pointers and wake alarm */
val = readl(pmc_base + PMC_UHSIC_TRIGGERS(inst));
val |= UHSIC_CLR_WAKE_ALARM(inst) | UHSIC_CLR_WALK_PTR(inst);
writel(val, pmc_base + PMC_UHSIC_TRIGGERS(inst));
/* Turn over pad configuration to PMC for line wake events*/
val = readl(pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
val |= UHSIC_MASTER_ENABLE(inst);
writel(val, pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
spin_unlock_irqrestore(&pmc_lock, flags);
DBG("%s:PMC enabled for HSIC remote wakeup\n", __func__);
}
static void uhsic_phy_disable_pmc_bus_ctrl(struct tegra_usb_pmc_data *pmc_data,
int enable_sof)
{
unsigned long val;
void __iomem *usb_base;
unsigned int inst = pmc_data->instance;
usb_base = pmc_data->usb_base;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, pmc_data->instance);
spin_lock_irqsave(&pmc_lock, flags);
val = readl(pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
if (!(val & UHSIC_MASTER_ENABLE(inst))) {
DBG("%s(%d) inst:[%d] pmc already disabled\n",
__func__, __LINE__, pmc_data->instance);
spin_unlock_irqrestore(&pmc_lock, flags);
return;
}
if (pmc_data->controller_type == TEGRA_USB_2_0 && usb_base) {
/* disable PMC master control */
val = readl(usb_base + UTMIP_PMC_WAKEUP0);
val &= ~EVENT_INT_ENB;
writel(val, usb_base + UTMIP_PMC_WAKEUP0);
}
val = readl(pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
val &= ~UHSIC_WAKE_VAL(inst, WAKE_VAL_ANY);
val |= UHSIC_WAKE_VAL(inst, WAKE_VAL_NONE);
writel(val, pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
/* Disable PMC master mode by clearing MASTER_EN */
val = readl(pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
val &= ~(UHSIC_MASTER_ENABLE(inst));
writel(val, pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
/* turn off pad detectors */
val = readl(pmc_base + PMC_USB_AO);
val |= (STROBE_VAL_PD(inst) | DATA_VAL_PD(inst));
writel(val, pmc_base + PMC_USB_AO);
val = readl(pmc_base + PMC_UHSIC_TRIGGERS(inst));
val |= (UHSIC_CLR_WALK_PTR(inst) | UHSIC_CLR_WAKE_ALARM(inst));
writel(val, pmc_base + PMC_UHSIC_TRIGGERS(inst));
if (pmc_data->controller_type == TEGRA_USB_2_0 && enable_sof == 1 &&
usb_base) {
val = readl(usb_base + USB_USBCMD);
val |= USB_USBCMD_RS;
writel(val, usb_base + USB_USBCMD);
}
spin_unlock_irqrestore(&pmc_lock, flags);
}
static struct tegra_usb_pmc_ops usb2_utmi_pmc_ops = {
.setup_pmc_wake_detect = utmip_setup_pmc_wake_detect,
.disable_pmc_bus_ctrl = utmip_phy_disable_pmc_bus_ctrl,
.powerup_pmc_wake_detect = utmip_powerup_pmc_wake_detect,
.powerdown_pmc_wake_detect = utmip_powerdown_pmc_wake_detect,
};
static struct tegra_usb_pmc_ops usb2_hsic_pmc_ops = {
.setup_pmc_wake_detect = uhsic_setup_pmc_wake_detect,
.disable_pmc_bus_ctrl = uhsic_phy_disable_pmc_bus_ctrl,
.powerup_pmc_wake_detect = uhsic_powerup_pmc_wake_detect,
.powerdown_pmc_wake_detect = uhsic_powerdown_pmc_wake_detect,
};
static struct tegra_usb_pmc_ops *pmc_ops[] = {
[TEGRA_USB_PHY_INTF_UTMI] = &usb2_utmi_pmc_ops,
[TEGRA_USB_PHY_INTF_HSIC] = &usb2_hsic_pmc_ops,
};
void tegra_usb_pmc_init(struct tegra_usb_pmc_data *pmc_data)
{
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, pmc_data->instance);
if (!pmc_base)
pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
#ifdef KERNEL_WARNING
pmc_data->pmc_ops.power_down_pmc = usb_phy_power_down_pmc;
#endif
pmc_data->pmc_ops = pmc_ops[pmc_data->phy_type];
}
EXPORT_SYMBOL_GPL(tegra_usb_pmc_init);
void tegra_usb_pmc_reg_update(u32 reg_offset, u32 mask, u32 val)
{
u32 reg;
if (!pmc_base)
pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
spin_lock_irqsave(&pmc_lock, flags);
reg = readl(pmc_base + reg_offset);
reg &= ~mask;
reg |= val;
writel(reg, pmc_base + reg_offset);
spin_unlock_irqrestore(&pmc_lock, flags);
}
EXPORT_SYMBOL_GPL(tegra_usb_pmc_reg_update);
u32 tegra_usb_pmc_reg_read(u32 reg_offset)
{
u32 reg;
if (!pmc_base)
pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
spin_lock_irqsave(&pmc_lock, flags);
reg = readl(pmc_base + reg_offset);
spin_unlock_irqrestore(&pmc_lock, flags);
return reg;
}
EXPORT_SYMBOL_GPL(tegra_usb_pmc_reg_read);
void tegra_usb_pmc_reg_write(u32 reg_offset, u32 val)
{
if (!pmc_base)
pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
spin_lock_irqsave(&pmc_lock, flags);
writel(val, pmc_base + reg_offset);
spin_unlock_irqrestore(&pmc_lock, flags);
}
EXPORT_SYMBOL_GPL(tegra_usb_pmc_reg_write);