2153 lines
63 KiB
C

/*
* Copyright (C) 2014 Google, Inc.
* Copyright (C) 2014 NVIDIA Corporation
*
* 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/slab.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/clk/tegra.h>
#include <linux/delay.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/tegra-soc.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/resource.h>
#include <linux/usb/phy.h>
#include <linux/usb/tegra_xusb_phy.h>
#include <linux/usb/tegra_usb_pmc.h>
#include <linux/usb/tegra_xusb.h>
struct tegra_xusb_hsic_config {
u8 rx_strobe_trim;
u8 rx_data_trim;
u8 tx_rtune_n;
u8 tx_rtune_p;
u8 tx_slew_n;
u8 tx_slew_p;
bool auto_term_en;
u8 strb_trim_val;
bool pretend_connect;
};
struct tegra_xusb_phy_calib_data {
u32 hs_curr_level_pad[TEGRA_XUSB_UTMI_COUNT];
u32 hs_iref_cap;
u32 hs_term_range_adj;
u32 hs_squelch_level;
};
struct tegra_xusb_phy_board_data {
unsigned long utmi_pads;
unsigned long hsic_pads;
unsigned long ss_pads;
/*
* SS0 or SS1 port may be mapped either to USB2_P0 or USB2_P1
* ss_portmap[0:3] = SS0 map, ss_portmap[4:7] = SS1 map
*/
u32 ss_portmap;
u32 lane_owner;
struct tegra_xusb_hsic_config hsic[TEGRA_XUSB_HSIC_COUNT];
u32 hs_xcvr_setup_offset;
};
struct tegra_xusb_phy_config {
bool shared_ss_lanes;
bool save_ctle_context;
bool release_utmi_in_elpg;
bool use_hs_src_clk2;
bool recalc_tctrl_rctrl;
int num_utmi_pads;
u32 rx_wander;
u32 rx_eq;
u32 cdr_cntl;
u32 dfe_cntl;
u32 hs_slew;
u32 ls_rslew_pad[TEGRA_XUSB_UTMI_COUNT];
u32 hs_disc_lvl;
u32 spare_in;
u32 pmc_portmap[TEGRA_XUSB_UTMI_COUNT];
int utmi_port_offset;
int hsic_port_offset;
const struct tegra_xusb_padctl_regs *padctl_offsets;
};
struct tegra_xusb_phy {
struct device *dev;
struct tegra_xhci_hcd *xhci;
struct usb_phy u_phy;
void __iomem *padctl_regs;
void __iomem *pad_regs;
struct regmap *clkrst_regs;
struct regmap *pmc_regs;
struct notifier_block mbox_nb;
int padctl_irq;
struct clk *ss_src_clk;
struct clk *ss_clk;
struct clk *pll_u_480M;
struct clk *clk_m;
struct clk *pad_clk;
struct clk *plle;
struct regulator *utmi_vbus[TEGRA_XUSB_UTMI_COUNT];
struct regulator *vddio_hsic;
/* DFE and CTLE context */
u8 ss_ctx_saved;
u8 tap1_val[TEGRA_XUSB_SS_COUNT];
u8 amp_val[TEGRA_XUSB_SS_COUNT];
u8 ctle_z_val[TEGRA_XUSB_SS_COUNT];
u8 ctle_g_val[TEGRA_XUSB_SS_COUNT];
/* UTMI context */
u32 utmip_tctrl_val;
u32 utmip_rctrl_val;
struct tegra_xusb_phy_board_data board_data;
struct tegra_xusb_phy_calib_data calib_data;
const struct tegra_xusb_phy_config *soc_config;
};
enum hsic_pad_pupd {
PUPD_DISABLE = 0,
PUPD_IDLE,
PUPD_RESET
};
static inline struct tegra_xusb_phy *phy_to_tegra(struct usb_phy *phy)
{
return container_of(phy, struct tegra_xusb_phy, u_phy);
}
static inline u32 padctl_readl(struct tegra_xusb_phy *tegra, u32 reg)
{
BUG_ON(reg == PADCTL_REG_NONE);
return readl(tegra->padctl_regs + reg);
}
static inline void padctl_writel(struct tegra_xusb_phy *tegra, u32 val, u32 reg)
{
BUG_ON(reg == PADCTL_REG_NONE);
writel(val, tegra->padctl_regs + reg);
}
static inline u32 pad_readl(struct tegra_xusb_phy *tegra, u32 reg)
{
return readl(tegra->pad_regs + reg);
}
static inline void pad_writel(struct tegra_xusb_phy *tegra, u32 val, u32 reg)
{
writel(val, tegra->pad_regs + reg);
}
static inline u32 clkrst_readl(struct tegra_xusb_phy *tegra, u32 reg)
{
u32 val;
regmap_read(tegra->clkrst_regs, reg, &val);
return val;
}
static inline void clkrst_writel(struct tegra_xusb_phy *tegra, u32 val, u32 reg)
{
regmap_write(tegra->clkrst_regs, reg, val);
}
static inline u32 pmc_readl(struct tegra_xusb_phy *tegra, u32 reg)
{
u32 val;
regmap_read(tegra->pmc_regs, reg, &val);
return val;
}
static inline void pmc_writel(struct tegra_xusb_phy *tegra, u32 val, u32 reg)
{
regmap_write(tegra->pmc_regs, reg, val);
}
static void clear_wake_interrupts(struct tegra_xusb_phy *tegra)
{
const struct tegra_xusb_padctl_regs *padregs;
u32 elpg_program0;
padregs = tegra->soc_config->padctl_offsets;
elpg_program0 = padctl_readl(tegra, padregs->elpg_program_0);
elpg_program0 |= WAKEUP_EVENT_MASK;
padctl_writel(tegra, elpg_program0, padregs->elpg_program_0);
}
static void disable_wake_interrupts(struct tegra_xusb_phy *tegra)
{
const struct tegra_xusb_padctl_regs *padregs;
u32 elpg_program0;
unsigned long ss_pads = tegra->board_data.ss_pads;
unsigned long hsic_pads = tegra->board_data.hsic_pads;
unsigned long utmi_pads = tegra->board_data.utmi_pads;
int i;
padregs = tegra->soc_config->padctl_offsets;
clear_wake_interrupts(tegra);
elpg_program0 = padctl_readl(tegra, padregs->elpg_program_0);
for_each_set_bit(i, &ss_pads, TEGRA_XUSB_SS_COUNT)
elpg_program0 &= ~SS_PORT_WAKE_INTERRUPT_ENABLE(i);
for_each_set_bit(i, &hsic_pads, TEGRA_XUSB_HSIC_COUNT)
elpg_program0 &= ~USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(i);
for_each_set_bit(i, &utmi_pads, TEGRA_XUSB_UTMI_COUNT)
elpg_program0 &= ~USB2_PORT_WAKE_INTERRUPT_ENABLE(i);
padctl_writel(tegra, elpg_program0, padregs->elpg_program_0);
}
static void enable_wake_interrupts(struct tegra_xusb_phy *tegra)
{
const struct tegra_xusb_padctl_regs *padregs;
u32 elpg_program0;
unsigned long ss_pads = tegra->board_data.ss_pads;
unsigned long hsic_pads = tegra->board_data.hsic_pads;
unsigned long utmi_pads = tegra->board_data.utmi_pads;
int i;
padregs = tegra->soc_config->padctl_offsets;
clear_wake_interrupts(tegra);
elpg_program0 = padctl_readl(tegra, padregs->elpg_program_0);
for_each_set_bit(i, &ss_pads, TEGRA_XUSB_SS_COUNT)
elpg_program0 |= SS_PORT_WAKE_INTERRUPT_ENABLE(i);
for_each_set_bit(i, &hsic_pads, TEGRA_XUSB_HSIC_COUNT)
elpg_program0 |= USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(i);
for_each_set_bit(i, &utmi_pads, TEGRA_XUSB_UTMI_COUNT)
elpg_program0 |= USB2_PORT_WAKE_INTERRUPT_ENABLE(i);
padctl_writel(tegra, elpg_program0, padregs->elpg_program_0);
}
static void hsic_pad_enable(struct tegra_xusb_phy *tegra, u8 pad)
{
const struct tegra_xusb_padctl_regs *padregs;
struct tegra_xusb_hsic_config *hsic = &tegra->board_data.hsic[pad];
u32 reg;
padregs = tegra->soc_config->padctl_offsets;
reg = padctl_readl(tegra, padregs->usb2_hsic_padX_ctlY_0[pad][2]);
reg &= ~(USB2_HSIC_RX_STROBE_TRIM(~0) | USB2_HSIC_RX_DATA_TRIM(~0));
reg |= USB2_HSIC_RX_STROBE_TRIM(hsic->rx_strobe_trim);
reg |= USB2_HSIC_RX_DATA_TRIM(hsic->rx_data_trim);
padctl_writel(tegra, reg, padregs->usb2_hsic_padX_ctlY_0[pad][2]);
reg = padctl_readl(tegra, padregs->usb2_hsic_padX_ctlY_0[pad][0]);
reg &= ~(USB2_HSIC_TX_RTUNEP(~0) | USB2_HSIC_TX_RTUNEN(~0) |
USB2_HSIC_TX_SLEWP(~0) | USB2_HSIC_TX_SLEWN(~0));
reg |= USB2_HSIC_TX_RTUNEP(hsic->tx_rtune_p);
reg |= USB2_HSIC_TX_RTUNEN(hsic->tx_rtune_n);
reg |= USB2_HSIC_TX_SLEWP(hsic->tx_slew_p);
reg |= USB2_HSIC_TX_SLEWN(hsic->tx_slew_n);
padctl_writel(tegra, reg, padregs->usb2_hsic_padX_ctlY_0[pad][0]);
reg = padctl_readl(tegra, padregs->usb2_hsic_padX_ctlY_0[pad][1]);
reg &= ~(USB2_HSIC_RPD_DATA | USB2_HSIC_RPD_STROBE |
USB2_HSIC_RPU_DATA | USB2_HSIC_RPU_STROBE);
/* Keep HSIC in IDLE */
reg |= (USB2_HSIC_RPD_DATA | USB2_HSIC_RPU_STROBE);
if (hsic->auto_term_en)
reg |= USB2_HSIC_AUTO_TERM_EN;
else
reg &= ~USB2_HSIC_AUTO_TERM_EN;
reg &= ~(USB2_HSIC_PD_RX | USB2_HSIC_PD_ZI |
USB2_HSIC_PD_TRX | USB2_HSIC_PD_TX);
padctl_writel(tegra, reg, padregs->usb2_hsic_padX_ctlY_0[pad][1]);
reg = padctl_readl(tegra, padregs->hsic_strb_trim_ctl0);
reg &= ~(HSIC_STRB_TRIM_VAL(~0));
reg |= HSIC_STRB_TRIM_VAL(hsic->strb_trim_val);
padctl_writel(tegra, reg, padregs->hsic_strb_trim_ctl0);
reg = padctl_readl(tegra, padregs->usb2_pad_mux_0);
reg |= USB2_HSIC_PAD_PORT(pad);
padctl_writel(tegra, reg, padregs->usb2_pad_mux_0);
}
static void hsic_pad_disable(struct tegra_xusb_phy *tegra, u8 pad)
{
const struct tegra_xusb_padctl_regs *padregs;
u32 reg;
padregs = tegra->soc_config->padctl_offsets;
reg = padctl_readl(tegra, padregs->usb2_pad_mux_0);
reg &= ~USB2_HSIC_PAD_PORT(pad);
padctl_writel(tegra, reg, padregs->usb2_pad_mux_0);
reg = padctl_readl(tegra, padregs->usb2_hsic_padX_ctlY_0[pad][1]);
reg |= (USB2_HSIC_PD_RX | USB2_HSIC_PD_ZI | USB2_HSIC_PD_TRX |
USB2_HSIC_PD_TX);
padctl_writel(tegra, reg, padregs->usb2_hsic_padX_ctlY_0[pad][1]);
}
static int hsic_pad_set_pupd(struct tegra_xusb_phy *tegra, u8 pad,
enum hsic_pad_pupd pupd)
{
const struct tegra_xusb_padctl_regs *padregs;
u32 reg;
padregs = tegra->soc_config->padctl_offsets;
reg = padctl_readl(tegra, padregs->usb2_hsic_padX_ctlY_0[pad][1]);
reg &= ~(USB2_HSIC_RPD_DATA | USB2_HSIC_RPD_STROBE |
USB2_HSIC_RPU_DATA | USB2_HSIC_RPU_STROBE);
if (pupd == PUPD_IDLE)
reg |= (USB2_HSIC_RPD_DATA | USB2_HSIC_RPU_STROBE);
else if (pupd == PUPD_RESET)
reg |= (USB2_HSIC_RPD_DATA | USB2_HSIC_RPD_STROBE);
else if (pupd != PUPD_DISABLE) {
dev_err(tegra->dev, "invalid hsic pupd %d\n", pupd);
return -EINVAL;
}
padctl_writel(tegra, reg, padregs->usb2_hsic_padX_ctlY_0[pad][1]);
return 0;
}
static void utmi_calc_tctrl_rctrl(struct tegra_xusb_phy *tegra)
{
const struct tegra_xusb_padctl_regs *padregs;
u32 reg, utmi_pads, utmi_mask;
padregs = tegra->soc_config->padctl_offsets;
/* Use XUSB_PADCTL space only when XUSB owns all UTMIP port */
utmi_pads = tegra->board_data.utmi_pads;
utmi_mask = (1 << tegra->soc_config->num_utmi_pads) - 1;
if ((utmi_pads & utmi_mask) == utmi_mask) {
/*
* USB2_BIAS_PAD_CTL0_0_PD = 0
* USB2_BIAS_PAD_CTL0_0_PD_TRK = 0
*/
reg = padctl_readl(tegra, padregs->usb2_bias_pad_ctlY_0[0]);
reg &= ~(USB2_BIAS_PD | USB2_BIAS_PD_TRK);
padctl_writel(tegra, reg, padregs->usb2_bias_pad_ctlY_0[0]);
/* Wait 20us */
usleep_range(20, 30);
/*
* Read USB2_BIAS_PAD_CTL1_0_{TCTRL,RCTRL}
*/
reg = padctl_readl(tegra, padregs->usb2_bias_pad_ctlY_0[1]);
tegra->utmip_rctrl_val = 0xf + ffz(USB2_BIAS_RCTRL_VAL(reg));
tegra->utmip_tctrl_val = 0xf + ffz(USB2_BIAS_TCTRL_VAL(reg));
reg = padctl_readl(tegra, padregs->usb2_bias_pad_ctlY_0[0]);
reg |= USB2_BIAS_PD_TRK;
padctl_writel(tegra, reg, padregs->usb2_bias_pad_ctlY_0[0]);
/*
* Program thermally encoded RCTRL_VAL, TCTRL_VAL into PMC
* space and set the PMC override.
*/
reg = PMC_TCTRL_VAL(tegra->utmip_tctrl_val) |
PMC_RCTRL_VAL(tegra->utmip_rctrl_val);
pmc_writel(tegra, reg, PMC_UTMIP_TERM_PAD_CFG);
reg = pmc_readl(tegra, PMC_SLEEP_CFG);
reg |= UTMIP_RCTRL_USE_PMC_P2 | UTMIP_TCTRL_USE_PMC_P2;
pmc_writel(tegra, reg, PMC_SLEEP_CFG);
} else {
/* Use common PMC API to use SNPS register space */
clk_prepare_enable(tegra->pad_clk);
/* Bias pad MASTER_ENABLE=1 */
reg = pmc_readl(tegra, PMC_UTMIP_BIAS_MASTER_CNTRL);
reg |= BIAS_MASTER_PROG_VAL;
pmc_writel(tegra, reg, PMC_UTMIP_BIAS_MASTER_CNTRL);
/* Set the tracking length time */
reg = pad_readl(tegra, UTMIP_BIAS_CFG1);
reg &= ~UTMIP_BIAS_PDTRK_COUNT(~0);
reg |= UTMIP_BIAS_PDTRK_COUNT(5);
pad_writel(tegra, reg, UTMIP_BIAS_CFG1);
/* Bias PDTRK is shared and MUST be done from USB1 ONLY */
reg = pad_readl(tegra, UTMIP_BIAS_CFG1);
reg &= ~UTMIP_BIAS_PDTRK_POWERDOWN;
pad_writel(tegra, reg, UTMIP_BIAS_CFG1);
reg = pad_readl(tegra, UTMIP_BIAS_CFG1);
reg |= UTMIP_BIAS_PDTRK_POWERUP;
pad_writel(tegra, reg, UTMIP_BIAS_CFG1);
/* Wait for 25usec */
udelay(25);
/* Bias pad MASTER_ENABLE=0 */
reg = pmc_readl(tegra, PMC_UTMIP_BIAS_MASTER_CNTRL);
reg &= ~BIAS_MASTER_PROG_VAL;
pmc_writel(tegra, reg, PMC_UTMIP_BIAS_MASTER_CNTRL);
/* Wait for 1usec */
udelay(1);
/* Bias pad MASTER_ENABLE=1 */
reg = pmc_readl(tegra, PMC_UTMIP_BIAS_MASTER_CNTRL);
reg |= BIAS_MASTER_PROG_VAL;
pmc_writel(tegra, reg, PMC_UTMIP_BIAS_MASTER_CNTRL);
/* Read RCTRL and TCTRL from UTMIP space */
reg = pad_readl(tegra, UTMIP_BIAS_STS0);
tegra->utmip_rctrl_val = 0xf + ffz(UTMIP_RCTRL_VAL(reg));
tegra->utmip_tctrl_val = 0xf + ffz(UTMIP_TCTRL_VAL(reg));
/* PD_TRK=1 */
reg = pad_readl(tegra, UTMIP_BIAS_CFG1);
reg |= UTMIP_BIAS_PDTRK_POWERDOWN;
pad_writel(tegra, reg, UTMIP_BIAS_CFG1);
/*
* Program thermally encoded RCTRL_VAL, TCTRL_VAL into PMC
* space.
*/
reg = PMC_TCTRL_VAL(tegra->utmip_tctrl_val) |
PMC_RCTRL_VAL(tegra->utmip_rctrl_val);
pmc_writel(tegra, reg, PMC_UTMIP_TERM_PAD_CFG);
clk_disable_unprepare(tegra->pad_clk);
}
dev_dbg(tegra->dev, "rctrl = 0x%x, tctrl = 0x%x\n",
tegra->utmip_rctrl_val, tegra->utmip_tctrl_val);
}
static void utmi_phy_iddq_override(struct tegra_xusb_phy *tegra, bool set)
{
u32 val;
val = clkrst_readl(tegra, UTMIPLL_HW_PWRDN_CFG0);
if (set)
val |= UTMIPLL_IDDQ_OVERRIDE;
else
val &= ~UTMIPLL_IDDQ_OVERRIDE;
val |= UTMIPLL_IDDQ_SWCTL;
clkrst_writel(tegra, val, UTMIPLL_HW_PWRDN_CFG0);
}
static void utmi_pads_enable(struct tegra_xusb_phy *tegra)
{
const struct tegra_xusb_padctl_regs *padregs;
u32 val;
padregs = tegra->soc_config->padctl_offsets;
clk_prepare_enable(tegra->pad_clk);
val = pad_readl(tegra, UTMIP_BIAS_CFG0);
val &= ~(UTMIP_OTGPD | UTMIP_BIASPD);
val |= UTMIP_HSSQUELCH_LEVEL(0x2) | UTMIP_HSDISCON_LEVEL(0x1) |
UTMIP_HSDISCON_LEVEL_MSB;
pad_writel(tegra, val, UTMIP_BIAS_CFG0);
val = padctl_readl(tegra, padregs->usb2_bias_pad_ctlY_0[0]);
val &= ~USB2_BIAS_PD;
padctl_writel(tegra, val, padregs->usb2_bias_pad_ctlY_0[0]);
clk_disable_unprepare(tegra->pad_clk);
utmi_phy_iddq_override(tegra, false);
}
static void utmi_pads_disable(struct tegra_xusb_phy *tegra)
{
const struct tegra_xusb_padctl_regs *padregs;
u32 val;
padregs = tegra->soc_config->padctl_offsets;
clk_prepare_enable(tegra->pad_clk);
val = pad_readl(tegra, UTMIP_BIAS_CFG0);
val |= UTMIP_OTGPD | UTMIP_BIASPD;
val &= ~(UTMIP_HSSQUELCH_LEVEL(~0) | UTMIP_HSDISCON_LEVEL(~0) |
UTMIP_HSDISCON_LEVEL_MSB);
pad_writel(tegra, val, UTMIP_BIAS_CFG0);
val = padctl_readl(tegra, padregs->usb2_bias_pad_ctlY_0[0]);
val |= USB2_BIAS_PD;
padctl_writel(tegra, val, padregs->usb2_bias_pad_ctlY_0[0]);
clk_disable_unprepare(tegra->pad_clk);
utmi_phy_iddq_override(tegra, true);
}
static void utmi_pad_init(struct tegra_xusb_phy *tegra, u8 port)
{
const struct tegra_xusb_padctl_regs *padregs;
u32 reg;
u32 ctl0_offset, ctl1_offset;
struct tegra_xusb_phy_board_data *bdata = &tegra->board_data;
u32 val;
padregs = tegra->soc_config->padctl_offsets;
reg = padctl_readl(tegra, padregs->usb2_pad_mux_0);
reg &= ~USB2_OTG_PAD_PORT_MASK(port);
reg |= USB2_OTG_PAD_PORT_OWNER_XUSB(port);
padctl_writel(tegra, reg, padregs->usb2_pad_mux_0);
reg = padctl_readl(tegra, padregs->usb2_port_cap_0);
reg &= ~USB2_PORT_CAP_MASK(port);
reg |= USB2_PORT_CAP_HOST(port);
padctl_writel(tegra, reg, padregs->usb2_port_cap_0);
ctl0_offset = padregs->usb2_otg_padX_ctlY_0[port][0];
ctl1_offset = padregs->usb2_otg_padX_ctlY_0[port][1];
reg = padctl_readl(tegra, ctl0_offset);
reg &= ~(USB2_OTG_HS_CURR_LVL | USB2_OTG_HS_SLEW |
USB2_OTG_FS_SLEW | USB2_OTG_LS_RSLEW |
USB2_OTG_PD | USB2_OTG_PD2 | USB2_OTG_PD_ZI);
reg |= tegra->soc_config->hs_slew;
reg |= tegra->soc_config->ls_rslew_pad[port];
val = (bdata->hs_xcvr_setup_offset >> (8 * port)) & 0xff;
if ((tegra->calib_data.hs_curr_level_pad[port] + val) >
USB2_OTG_HS_CURR_LVL_MAX)
dev_warn(tegra->dev,
"0x%X in nvidia,xusb-hs-xcvr-setup-offset too large\n",
val);
reg |= min(tegra->calib_data.hs_curr_level_pad[port] + val,
(u32) USB2_OTG_HS_CURR_LVL_MAX);
padctl_writel(tegra, reg, ctl0_offset);
reg = padctl_readl(tegra, ctl1_offset);
reg &= ~(USB2_OTG_TERM_RANGE_AD | USB2_OTG_HS_IREF_CAP
| USB2_OTG_PD_CHRP_FORCE_POWERUP
| USB2_OTG_PD_DISC_FORCE_POWERUP
| USB2_OTG_PD_DR);
reg |= (tegra->calib_data.hs_iref_cap << 9) |
(tegra->calib_data.hs_term_range_adj << 3);
padctl_writel(tegra, reg, ctl1_offset);
}
static void utmi_pads_release(struct tegra_xusb_phy *tegra)
{
const struct tegra_xusb_padctl_regs *padregs;
unsigned long reg;
if (!tegra->soc_config->release_utmi_in_elpg)
return;
padregs = tegra->soc_config->padctl_offsets;
reg = padctl_readl(tegra, padregs->usb2_pad_mux_0);
reg &= ~(USB2_OTG_PAD_PORT_MASK(0) | USB2_OTG_PAD_PORT_MASK(1) |
USB2_OTG_PAD_PORT_MASK(2));
padctl_writel(tegra, reg, padregs->usb2_pad_mux_0);
}
static void ss_save_context(struct tegra_xusb_phy *tegra, u8 port)
{
const struct tegra_xusb_padctl_regs *padregs;
u32 offset;
u32 reg;
padregs = tegra->soc_config->padctl_offsets;
tegra->ss_ctx_saved |= BIT(port);
dev_dbg(tegra->dev, "Saving DFE context of port %d\n", port);
/* If port1 is mapped to SATA lane then read from SATA register */
if (port == 1 && tegra->soc_config->shared_ss_lanes &&
tegra->board_data.lane_owner & BIT(0))
offset = padregs->iophy_misc_pad_s0_ctlY_0[5];
else
offset = padregs->iophy_misc_pad_pX_ctlY_0[port][5];
reg = padctl_readl(tegra, offset);
reg &= ~IOPHY_MISC_OUT_SEL(~0);
reg |= IOPHY_MISC_OUT_SEL(IOPHY_MISC_OUT_SEL_TAP);
padctl_writel(tegra, reg, offset);
reg = padctl_readl(tegra, offset);
tegra->tap1_val[port] = IOPHY_MISC_OUT_TAP_VAL(reg);
reg = padctl_readl(tegra, offset);
reg &= ~IOPHY_MISC_OUT_SEL(~0);
reg |= IOPHY_MISC_OUT_SEL(IOPHY_MISC_OUT_SEL_AMP);
padctl_writel(tegra, reg, offset);
reg = padctl_readl(tegra, offset);
tegra->amp_val[port] = IOPHY_MISC_OUT_AMP_VAL(reg);
reg = padctl_readl(tegra, padregs->iophy_usb3_padX_ctlY_0[port][3]);
reg &= ~IOPHY_USB3_DFE_CNTL_TAP_VAL(~0);
reg |= IOPHY_USB3_DFE_CNTL_TAP_VAL(tegra->tap1_val[port]);
padctl_writel(tegra, reg, padregs->iophy_usb3_padX_ctlY_0[port][3]);
reg = padctl_readl(tegra, padregs->iophy_usb3_padX_ctlY_0[port][3]);
reg &= ~IOPHY_USB3_DFE_CNTL_AMP_VAL(~0);
reg |= IOPHY_USB3_DFE_CNTL_AMP_VAL(tegra->amp_val[port]);
padctl_writel(tegra, reg, padregs->iophy_usb3_padX_ctlY_0[port][3]);
if (!tegra->soc_config->save_ctle_context)
return;
dev_dbg(tegra->dev, "Saving restore CTLE context of port %d\n", port);
reg = padctl_readl(tegra, offset);
reg &= ~IOPHY_MISC_OUT_SEL(~0);
reg |= IOPHY_MISC_OUT_SEL(IOPHY_MISC_OUT_SEL_LATCH_G_Z);
padctl_writel(tegra, reg, offset);
reg = padctl_readl(tegra, offset);
reg &= ~IOPHY_MISC_OUT_SEL(~0);
reg |= IOPHY_MISC_OUT_SEL(IOPHY_MISC_OUT_SEL_G_Z);
padctl_writel(tegra, reg, offset);
reg = padctl_readl(tegra, offset);
tegra->ctle_g_val[port] = IOPHY_MISC_OUT_G_Z_VAL(reg);
reg = padctl_readl(tegra, offset);
reg &= ~IOPHY_MISC_OUT_SEL(~0);
reg |= IOPHY_MISC_OUT_SEL(IOPHY_MISC_OUT_SEL_CTLE_Z);
padctl_writel(tegra, reg, offset);
reg = padctl_readl(tegra, offset);
tegra->ctle_z_val[port] = IOPHY_MISC_OUT_G_Z_VAL(reg);
reg = padctl_readl(tegra, padregs->iophy_usb3_padX_ctlY_0[port][1]);
reg &= ~IOPHY_USB3_RX_EQ_Z_VAL(~0);
reg |= IOPHY_USB3_RX_EQ_Z_VAL(tegra->ctle_z_val[port]);
padctl_writel(tegra, reg, padregs->iophy_usb3_padX_ctlY_0[port][1]);
reg = padctl_readl(tegra, padregs->iophy_usb3_padX_ctlY_0[port][1]);
reg &= ~IOPHY_USB3_RX_EQ_G_VAL(~0);
reg |= IOPHY_USB3_RX_EQ_G_VAL(tegra->ctle_g_val[port]);
padctl_writel(tegra, reg, padregs->iophy_usb3_padX_ctlY_0[port][1]);
}
static void ss_restore_context(struct tegra_xusb_phy *tegra, u8 port)
{
const struct tegra_xusb_padctl_regs *padregs;
u32 reg;
/* Don't restore if not saved */
if (!(tegra->ss_ctx_saved & BIT(port)))
return;
padregs = tegra->soc_config->padctl_offsets;
dev_dbg(tegra->dev, "Restoring DFE context of port %d\n", port);
reg = padctl_readl(tegra, padregs->iophy_usb3_padX_ctlY_0[port][3]);
reg &= ~(IOPHY_USB3_DFE_CNTL_AMP_VAL(~0) |
IOPHY_USB3_DFE_CNTL_TAP_VAL(~0));
reg |= IOPHY_USB3_DFE_CNTL_AMP_VAL(tegra->amp_val[port]) |
IOPHY_USB3_DFE_CNTL_TAP_VAL(tegra->tap1_val[port]);
padctl_writel(tegra, reg, padregs->iophy_usb3_padX_ctlY_0[port][3]);
if (!tegra->soc_config->save_ctle_context)
return;
dev_dbg(tegra->dev, "Restoring CTLE context of port %d\n", port);
reg = padctl_readl(tegra, padregs->iophy_usb3_padX_ctlY_0[port][1]);
reg &= ~(IOPHY_USB3_RX_EQ_Z_VAL(~0) | IOPHY_USB3_RX_EQ_G_VAL(~0));
reg |= (IOPHY_USB3_RX_EQ_Z_VAL(tegra->ctle_z_val[port]) |
IOPHY_USB3_RX_EQ_G_VAL(tegra->ctle_g_val[port]));
padctl_writel(tegra, reg, padregs->iophy_usb3_padX_ctlY_0[port][1]);
}
static inline bool use_sata_lane(struct tegra_xusb_phy *tegra)
{
return tegra->soc_config->shared_ss_lanes &&
(tegra->board_data.lane_owner & BIT(0)) &&
(tegra->board_data.ss_pads & BIT(1));
}
static void ss_set_clamp(struct tegra_xusb_phy *tegra, bool on)
{
const struct tegra_xusb_padctl_regs *padregs;
u32 elpg_program0;
int i;
padregs = tegra->soc_config->padctl_offsets;
/* Assert/Deassert clamp_en_early signals to SSP0/1 */
elpg_program0 = padctl_readl(tegra, padregs->elpg_program_0);
for_each_set_bit(i, &tegra->board_data.ss_pads, TEGRA_XUSB_SS_COUNT) {
if (on)
elpg_program0 |= SSP_ELPG_CLAMP_EN_EARLY(i);
else
elpg_program0 &= ~SSP_ELPG_CLAMP_EN_EARLY(i);
}
padctl_writel(tegra, elpg_program0, padregs->elpg_program_0);
/*
* Check the LP0 figure and leave gap bw writes to
* clamp_en_early and clamp_en
*/
udelay(100);
/* Assert/Deassert clam_en signal */
elpg_program0 = padctl_readl(tegra, padregs->elpg_program_0);
for_each_set_bit(i, &tegra->board_data.ss_pads, TEGRA_XUSB_SS_COUNT) {
if (on)
elpg_program0 |= SSP_ELPG_CLAMP_EN(i);
else
elpg_program0 &= ~SSP_ELPG_CLAMP_EN(i);
}
padctl_writel(tegra, elpg_program0, padregs->elpg_program_0);
/* Wait for 250us for the writes to propogate */
if (on)
udelay(250);
}
static void ss_set_vcore(struct tegra_xusb_phy *tegra, bool on)
{
const struct tegra_xusb_padctl_regs *padregs;
u32 elpg_program0;
int i;
padregs = tegra->soc_config->padctl_offsets;
/* Assert vcore_off signal */
elpg_program0 = padctl_readl(tegra, padregs->elpg_program_0);
for_each_set_bit(i, &tegra->board_data.ss_pads, TEGRA_XUSB_SS_COUNT) {
if (on)
elpg_program0 &= ~SSP_ELPG_VCORE_DOWN(i);
else
elpg_program0 |= SSP_ELPG_VCORE_DOWN(i);
}
padctl_writel(tegra, elpg_program0, padregs->elpg_program_0);
}
static void ss_rx_idle_mode_override(struct tegra_xusb_phy *tegra, bool enable)
{
const struct tegra_xusb_padctl_regs *padregs;
u32 reg, offset;
int i;
if (tegra->soc_config->shared_ss_lanes)
return;
padregs = tegra->soc_config->padctl_offsets;
for_each_set_bit(i, &tegra->board_data.ss_pads, TEGRA_XUSB_SS_COUNT) {
offset = padregs->iophy_misc_pad_pX_ctlY_0[i][2];
reg = padctl_readl(tegra, offset);
if (enable) {
reg &= ~IOPHY_RX_IDLE_MODE;
reg |= IOPHY_RX_IDLE_MODE_OVRD;
} else {
reg |= IOPHY_RX_IDLE_MODE;
reg &= ~IOPHY_RX_IDLE_MODE_OVRD;
}
padctl_writel(tegra, reg, offset);
}
}
static int ss_set_clock_rate(struct tegra_xusb_phy *tegra, unsigned int rate)
{
unsigned int new_parent_rate, old_parent_rate, div;
int ret;
struct clk *ss_clk = tegra->ss_src_clk;
if (clk_get_rate(ss_clk) == rate)
return 0;
switch (rate) {
case SS_CLK_HIGH_SPEED:
/* Reparent to PLLU_480M. Set div first to avoid overclocking */
old_parent_rate = clk_get_rate(clk_get_parent(ss_clk));
new_parent_rate = clk_get_rate(tegra->pll_u_480M);
div = new_parent_rate / rate;
ret = clk_set_rate(ss_clk, old_parent_rate / div);
if (ret) {
dev_err(tegra->dev, "Failed to set SS rate: %d\n",
ret);
return ret;
}
ret = clk_set_parent(ss_clk, tegra->pll_u_480M);
if (ret) {
dev_err(tegra->dev, "Failed to set SS parent: %d\n",
ret);
return ret;
}
ss_rx_idle_mode_override(tegra, false);
break;
case SS_CLK_LOW_SPEED:
/* Reparent to CLK_M */
ret = clk_set_parent(ss_clk, tegra->clk_m);
if (ret) {
dev_err(tegra->dev, "Failed to set SS parent: %d\n",
ret);
return ret;
}
ret = clk_set_rate(ss_clk, rate);
if (ret) {
dev_err(tegra->dev, "Failed to set SS rate: %d\n",
ret);
return ret;
}
ss_rx_idle_mode_override(tegra, true);
break;
default:
dev_err(tegra->dev, "Invalid SS rate: %u\n", rate);
return -EINVAL;
}
if (clk_get_rate(ss_clk) != rate) {
dev_err(tegra->dev, "SS clock doesn't match requested rate\n");
return -EINVAL;
}
return 0;
}
static void ss_pad_init(struct tegra_xusb_phy *tegra, u8 port)
{
const struct tegra_xusb_padctl_regs *padregs;
u32 ctl2_offset, ctl4_offset, misc_ctl5_offset, misc_ctl2_offset;
u32 reg;
padregs = tegra->soc_config->padctl_offsets;
ctl2_offset = padregs->iophy_usb3_padX_ctlY_0[port][1];
ctl4_offset = padregs->iophy_usb3_padX_ctlY_0[port][3];
misc_ctl5_offset = padregs->iophy_misc_pad_pX_ctlY_0[port][4];
misc_ctl2_offset = padregs->iophy_misc_pad_pX_ctlY_0[port][1];
reg = padctl_readl(tegra, ctl2_offset);
reg &= ~(IOPHY_USB3_RX_WANDER_VAL(~0) | IOPHY_USB3_RX_EQ_VAL(~0) |
IOPHY_USB3_CDR_CNTL_VAL(~0));
reg |= tegra->soc_config->rx_wander | tegra->soc_config->rx_eq |
tegra->soc_config->cdr_cntl;
padctl_writel(tegra, reg, ctl2_offset);
padctl_writel(tegra, tegra->soc_config->dfe_cntl, ctl4_offset);
reg = padctl_readl(tegra, misc_ctl5_offset);
reg |= IOPHY_RX_QEYE_EN;
padctl_writel(tegra, reg, misc_ctl5_offset);
reg = padctl_readl(tegra, misc_ctl2_offset);
reg &= ~IOPHY_SPARE_IN(~0);
reg |= IOPHY_SPARE_IN(tegra->soc_config->spare_in);
padctl_writel(tegra, reg, misc_ctl2_offset);
if (use_sata_lane(tegra)) {
reg = padctl_readl(tegra, padregs->iophy_misc_pad_s0_ctlY_0[4]);
reg |= IOPHY_RX_QEYE_EN;
padctl_writel(tegra, reg, padregs->iophy_misc_pad_s0_ctlY_0[4]);
reg = padctl_readl(tegra, padregs->iophy_misc_pad_s0_ctlY_0[1]);
reg &= ~IOPHY_SPARE_IN(~0);
reg |= IOPHY_SPARE_IN(tegra->soc_config->spare_in);
padctl_writel(tegra, reg, padregs->iophy_misc_pad_s0_ctlY_0[1]);
}
reg = padctl_readl(tegra, padregs->ss_port_map_0);
reg &= ~SS_PORT_MAP_P(port);
reg |= tegra->board_data.ss_portmap & SS_PORT_MAP_P(port);
padctl_writel(tegra, reg, padregs->ss_port_map_0);
ss_restore_context(tegra, port);
}
static void ss_lanes_init(struct tegra_xusb_phy *tegra)
{
const struct tegra_xusb_padctl_regs *padregs;
u32 lane_owner = tegra->board_data.lane_owner;
u32 val;
if (!tegra->soc_config->shared_ss_lanes)
return;
padregs = tegra->soc_config->padctl_offsets;
/* Program SATA pad phy */
if (lane_owner & BIT(0)) {
val = padctl_readl(tegra, padregs->iophy_pll_s0_ctlY_0[0]);
val &= ~IOPHY_PLL_PLL0_REFCLK_NDIV(~0);
val |= IOPHY_PLL_PLL0_REFCLK_NDIV(0x2);
padctl_writel(tegra, val, padregs->iophy_pll_s0_ctlY_0[0]);
val = padctl_readl(tegra, padregs->iophy_pll_s0_ctlY_0[1]);
val &= ~(IOPHY_PLL_XDIGCLK_SEL(~0) | IOPHY_PLL_TXCLKREF_SEL |
IOPHY_PLL_TCLKOUT_EN | IOPHY_PLL_PLL0_CP_CNTL(~0) |
IOPHY_PLL_PLL1_CP_CNTL(~0));
val |= IOPHY_PLL_XDIGCLK_SEL(0x7) | IOPHY_PLL_TXCLKREF_SEL |
IOPHY_PLL_PLL0_CP_CNTL(0x8) |
IOPHY_PLL_PLL1_CP_CNTL(0x8);
padctl_writel(tegra, val, padregs->iophy_pll_s0_ctlY_0[1]);
val = padctl_readl(tegra, padregs->iophy_pll_s0_ctlY_0[2]);
val &= ~IOPHY_PLL_RCAL_BYPASS;
padctl_writel(tegra, val, padregs->iophy_pll_s0_ctlY_0[2]);
/* Enable SATA PADPLL clocks */
val = clkrst_readl(tegra, SATA_PLL_CFG0_0);
val &= ~SATA_PADPLL_RESET_SWCTL;
val |= SATA_PADPLL_USE_LOCKDET | SATA_SEQ_START_STATE;
clkrst_writel(tegra, val, SATA_PLL_CFG0_0);
udelay(1);
val = clkrst_readl(tegra, SATA_PLL_CFG0_0);
val |= SATA_SEQ_ENABLE;
clkrst_writel(tegra, val, SATA_PLL_CFG0_0);
}
/*
* Program ownership of lanes owned by USB3 based on lane_owner[2:0]
* lane_owner[0] = 0 (SATA lane owner = SATA),
* lane_owner[0] = 1 (SATA lane owner = USB3_SS port1)
* lane_owner[1] = 0 (PCIe lane0 owner = PCIe),
* lane_owner[1] = 1 (PCIe lane0 owner = USB3_SS port0)
* lane_owner[2] = 0 (PCIe lane1 owner = PCIe),
* lane_owner[2] = 1 (PCIe lane1 owner = USB3_SS port1)
*/
val = padctl_readl(tegra, padregs->usb3_pad_mux_0);
/* USB3_SS port1 can either be mapped to SATA lane or PCIe lane1 */
if (lane_owner & BIT(0)) {
val &= ~USB3_SATA_PAD_LANE_OWNER(~0);
val |= USB3_SATA_PAD_LANE_OWNER(USB3_LANE_OWNER_USB3_SS);
} else if (lane_owner & BIT(2)) {
val &= ~USB3_PCIE_PAD_LANE_OWNER(1, ~0);
val |= USB3_PCIE_PAD_LANE_OWNER(1, USB3_LANE_OWNER_USB3_SS);
}
/* USB3_SS port0 is always mapped to PCIe lane0 */
if (lane_owner & BIT(1)) {
val &= ~USB3_PCIE_PAD_LANE_OWNER(0, ~0);
val |= USB3_PCIE_PAD_LANE_OWNER(0, USB3_LANE_OWNER_USB3_SS);
}
padctl_writel(tegra, val, padregs->usb3_pad_mux_0);
/* Bring enabled lane out of IDDQ */
val = padctl_readl(tegra, padregs->usb3_pad_mux_0);
if (lane_owner & BIT(0))
val |= USB3_FORCE_SATA_PAD_IDDQ_DISABLE_MASK;
else if (lane_owner & BIT(2))
val |= USB3_FORCE_PCIE_PAD_IDDQ_DISABLE_MASK(1);
if (lane_owner & BIT(1))
val |= USB3_FORCE_PCIE_PAD_IDDQ_DISABLE_MASK(0);
padctl_writel(tegra, val, padregs->usb3_pad_mux_0);
udelay(1);
/* Clear AUX_MUX_LP0 related bits in ELPG_PROGRAM */
val = padctl_readl(tegra, padregs->elpg_program_0);
val &= ~AUX_MUX_LP0_CLAMP_EN;
padctl_writel(tegra, val, padregs->elpg_program_0);
udelay(100);
val &= ~AUX_MUX_LP0_CLAMP_EN_EARLY;
padctl_writel(tegra, val, padregs->elpg_program_0);
udelay(100);
val &= ~AUX_MUX_LP0_VCORE_DOWN;
padctl_writel(tegra, val, padregs->elpg_program_0);
}
/*
* Assign the USB ports to the controllers, then programs the port
* capabilities and pad parameters.
*/
static void init_ports(struct tegra_xusb_phy *tegra)
{
const struct tegra_xusb_padctl_regs *padregs;
u32 reg, offset;
unsigned long ss_pads = tegra->board_data.ss_pads;
unsigned long hsic_pads = tegra->board_data.hsic_pads;
unsigned long utmi_pads = tegra->board_data.utmi_pads;
int i;
padregs = tegra->soc_config->padctl_offsets;
reg = padctl_readl(tegra, padregs->usb2_bias_pad_ctlY_0[0]);
reg &= ~(USB2_BIAS_HS_SQUELCH_LEVEL | USB2_BIAS_HS_DISCON_LEVEL);
reg |= tegra->calib_data.hs_squelch_level |
tegra->soc_config->hs_disc_lvl;
padctl_writel(tegra, reg, padregs->usb2_bias_pad_ctlY_0[0]);
for_each_set_bit(i, &utmi_pads, TEGRA_XUSB_UTMI_COUNT)
utmi_pad_init(tegra, i);
for_each_set_bit(i, &hsic_pads, TEGRA_XUSB_HSIC_COUNT)
hsic_pad_enable(tegra, i);
for (i = 0; i < TEGRA_XUSB_SS_COUNT; i++) {
if (ss_pads & BIT(i)) {
ss_pad_init(tegra, i);
} else {
/*
* Set rx_idle_mode_ovrd for unused SS ports to
* save power
*/
offset = padregs->iophy_misc_pad_pX_ctlY_0[i][2];
reg = padctl_readl(tegra, offset);
reg &= ~IOPHY_RX_IDLE_MODE;
reg |= IOPHY_RX_IDLE_MODE_OVRD;
padctl_writel(tegra, reg, offset);
/*
* SATA lane also if USB3_SS port1 mapped to it but
* unused
*/
if (i == 1 && tegra->soc_config->shared_ss_lanes &&
(tegra->board_data.lane_owner & BIT(0))) {
offset = padregs->iophy_misc_pad_s0_ctlY_0[2];
reg = padctl_readl(tegra, offset);
reg &= ~IOPHY_RX_IDLE_MODE;
reg |= IOPHY_RX_IDLE_MODE_OVRD;
padctl_writel(tegra, reg, offset);
}
}
}
ss_lanes_init(tegra);
}
static void hsic_pmc_wake_enable(struct tegra_xusb_phy *tegra, unsigned int pad)
{
u32 val, port;
if (!tegra_xhci_port_connected(tegra->xhci, pad +
tegra->soc_config->hsic_port_offset))
return;
port = pad + 1;
val = pmc_readl(tegra, PMC_UHSIC_SLEEP_CFG(port));
if (val & UHSIC_MASTER_ENABLE(port)) {
dev_info(tegra->dev, "HSIC wake already enabled on port %d\n",
port);
return;
}
/*
* Set PMC MASTER bits to do the following:
* a. Take over the UHSIC drivers
* b. Take over resume if remote wakeup is detected
* c. Take over suspend-wake detect-drive resume until USB controller
* ready.
*/
/* disable master enable in PMC */
val = pmc_readl(tegra, PMC_UHSIC_SLEEP_CFG(port));
val &= ~UHSIC_MASTER_ENABLE(port);
pmc_writel(tegra, val, PMC_UHSIC_SLEEP_CFG(port));
/* UTMIP_PWR_PX=1 for power savings mode */
val = pmc_readl(tegra, PMC_UHSIC_MASTER_CONFIG(port));
val |= UHSIC_PWR(port);
pmc_writel(tegra, val, PMC_UHSIC_MASTER_CONFIG(port));
/* config debouncer */
val = pmc_readl(tegra, PMC_USB_DEBOUNCE);
val |= PMC_USB_DEBOUNCE_VAL(2);
pmc_writel(tegra, val, PMC_USB_DEBOUNCE);
/* Make sure nothing is happening on the line with respect to PMC */
val = pmc_readl(tegra, PMC_UHSIC_FAKE(port));
val &= ~UHSIC_FAKE_STROBE_VAL(port);
val &= ~UHSIC_FAKE_DATA_VAL(port);
pmc_writel(tegra, val, PMC_UHSIC_FAKE(port));
/* Clear walk enable */
val = pmc_readl(tegra, PMC_UHSIC_SLEEPWALK_CFG(port));
val &= ~UHSIC_LINEVAL_WALK_EN(port);
pmc_writel(tegra, val, PMC_UHSIC_SLEEPWALK_CFG(port));
/* Make sure wake value for line is none */
val = pmc_readl(tegra, PMC_UHSIC_SLEEP_CFG(port));
val &= ~UHSIC_WAKE_VAL(port, WAKE_VAL_ANY);
val |= UHSIC_WAKE_VAL(port, WAKE_VAL_NONE);
pmc_writel(tegra, val, PMC_UHSIC_SLEEP_CFG(port));
/* turn on pad detectors */
val = pmc_readl(tegra, PMC_USB_AO);
val &= ~(STROBE_VAL_PD(port) | DATA_VAL_PD(port));
pmc_writel(tegra, val, 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 = pmc_readl(tegra, PMC_UHSIC_SLEEPWALK_CFG(port));
val |= UHSIC_LINEVAL_WALK_EN(port);
pmc_writel(tegra, val, PMC_UHSIC_SLEEPWALK_CFG(port));
/*
* Program walk sequence: maintain a J, followed by a driven K
* to signal a resume once an wake event is detected
*/
val = pmc_readl(tegra, PMC_SLEEPWALK_UHSIC(port));
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;
pmc_writel(tegra, val, PMC_SLEEPWALK_UHSIC(port));
/* Set wake event */
val = pmc_readl(tegra, PMC_UHSIC_SLEEP_CFG(port));
val &= ~UHSIC_WAKE_VAL(port, WAKE_VAL_ANY);
val |= UHSIC_WAKE_VAL(port, WAKE_VAL_SD10);
pmc_writel(tegra, val, PMC_UHSIC_SLEEP_CFG(port));
/* Clear the walk pointers and wake alarm */
val = pmc_readl(tegra, PMC_UHSIC_TRIGGERS(port));
val |= UHSIC_CLR_WAKE_ALARM(port) | UHSIC_CLR_WALK_PTR(port);
pmc_writel(tegra, val, PMC_UHSIC_TRIGGERS(port));
/* Turn over pad configuration to PMC for line wake events*/
val = pmc_readl(tegra, PMC_UHSIC_SLEEP_CFG(port));
val |= UHSIC_MASTER_ENABLE(port);
pmc_writel(tegra, val, PMC_UHSIC_SLEEP_CFG(port));
}
static void hsic_pmc_wake_disable(struct tegra_xusb_phy *tegra,
unsigned int pad)
{
u32 val, port;
port = pad + 1;
val = pmc_readl(tegra, PMC_UHSIC_SLEEP_CFG(port));
if (!(val & UHSIC_MASTER_ENABLE(port))) {
dev_info(tegra->dev, "HSIC wake already disabled on port %d\n",
port);
return;
}
val = pmc_readl(tegra, PMC_UHSIC_SLEEP_CFG(port));
val &= ~UHSIC_WAKE_VAL(port, WAKE_VAL_ANY);
val |= UHSIC_WAKE_VAL(port, WAKE_VAL_NONE);
pmc_writel(tegra, val, PMC_UHSIC_SLEEP_CFG(port));
/* Disable PMC master mode by clearing MASTER_EN */
val = pmc_readl(tegra, PMC_UHSIC_SLEEP_CFG(port));
val &= ~(UHSIC_MASTER_ENABLE(port));
pmc_writel(tegra, val, PMC_UHSIC_SLEEP_CFG(port));
/* Turn off pad detectors */
val = pmc_readl(tegra, PMC_USB_AO);
val |= (STROBE_VAL_PD(port) | DATA_VAL_PD(port));
pmc_writel(tegra, val, PMC_USB_AO);
val = pmc_readl(tegra, PMC_UHSIC_TRIGGERS(port));
val |= (UHSIC_CLR_WALK_PTR(port) | UHSIC_CLR_WAKE_ALARM(port));
pmc_writel(tegra, val, PMC_UHSIC_TRIGGERS(port));
}
static void utmi_pmc_wake_enable(struct tegra_xusb_phy *tegra, unsigned int pad)
{
u32 val, pmc_pad_cfg_val, port;
enum usb_device_speed port_speed;
port = tegra->soc_config->pmc_portmap[pad];
port_speed = tegra_xhci_port_speed(tegra->xhci, pad +
tegra->soc_config->utmi_port_offset);
val = pmc_readl(tegra, PMC_SLEEP_CFG);
if (val & UTMIP_MASTER_ENABLE(port)) {
dev_info(tegra->dev, "UTMI wake already enabled on port %d\n",
port);
return;
}
/*
* Set PMC MASTER bits to do the following:
* a. Take over the UTMI drivers
* b. Take over resume if remote wakeup is detected
* c. Take over suspend-wake detect-drive resume until USB controller
* ready.
*/
/* Disable master enable in PMC */
val = pmc_readl(tegra, PMC_SLEEP_CFG);
val &= ~UTMIP_MASTER_ENABLE(port);
pmc_writel(tegra, val, PMC_SLEEP_CFG);
/* UTMIP_PWR_PX=1 for power savings mode */
val = pmc_readl(tegra, PMC_UTMIP_MASTER_CONFIG);
val |= UTMIP_PWR(port);
pmc_writel(tegra, val, PMC_UTMIP_MASTER_CONFIG);
/* Config debouncer */
val = pmc_readl(tegra, PMC_USB_DEBOUNCE);
val &= ~UTMIP_LINE_DEB_CNT(~0);
val |= UTMIP_LINE_DEB_CNT(1);
val |= PMC_USB_DEBOUNCE_VAL(2);
pmc_writel(tegra, val, PMC_USB_DEBOUNCE);
/* Make sure nothing is happening on the line with respect to PMC */
val = pmc_readl(tegra, PMC_UTMIP_FAKE);
val &= ~USBOP_VAL(port);
val &= ~USBON_VAL(port);
pmc_writel(tegra, val, PMC_UTMIP_FAKE);
/* Make sure wake value for line is none */
val = pmc_readl(tegra, PMC_SLEEPWALK_CFG);
val &= ~UTMIP_LINEVAL_WALK_EN(port);
pmc_writel(tegra, val, PMC_SLEEPWALK_CFG);
val = pmc_readl(tegra, PMC_SLEEP_CFG);
val &= ~UTMIP_WAKE_VAL(port, ~0);
val |= UTMIP_WAKE_VAL(port, WAKE_VAL_NONE);
pmc_writel(tegra, val, PMC_SLEEP_CFG);
/* turn off pad detectors */
val = pmc_readl(tegra, PMC_USB_AO);
val |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port));
pmc_writel(tegra, val, PMC_USB_AO);
/* Remove fake values and make synchronizers work a bit */
val = pmc_readl(tegra, PMC_UTMIP_FAKE);
val &= ~USBOP_VAL(port);
val &= ~USBON_VAL(port);
pmc_writel(tegra, val, PMC_UTMIP_FAKE);
/*
* Enable which type of event can trigger a walk, in this case
* usb_line_wake
*/
val = pmc_readl(tegra, PMC_SLEEPWALK_CFG);
val |= UTMIP_LINEVAL_WALK_EN(port);
pmc_writel(tegra, val, PMC_SLEEPWALK_CFG);
/* Capture FS/LS pad configurations */
pmc_pad_cfg_val = pmc_readl(tegra, PMC_PAD_CFG);
val = pmc_readl(tegra, PMC_TRIGGERS);
val |= UTMIP_CAP_CFG(port);
pmc_writel(tegra, val, PMC_TRIGGERS);
udelay(1);
pmc_pad_cfg_val = pmc_readl(tegra, PMC_PAD_CFG);
/* BIAS MASTER_ENABLE=0 */
val = pmc_readl(tegra, PMC_UTMIP_BIAS_MASTER_CNTRL);
val &= ~BIAS_MASTER_PROG_VAL;
pmc_writel(tegra, val, PMC_UTMIP_BIAS_MASTER_CNTRL);
/* Program walk sequence for remote or hotplug wakeup */
if (port_speed > USB_SPEED_UNKNOWN) {
/*
* Program walk sequence: maintain a J, followed by a driven K
* to signal a resume once an wake event is detected
*/
val = pmc_readl(tegra, PMC_SLEEPWALK_REG(port));
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;
pmc_writel(tegra, val, PMC_SLEEPWALK_REG(port));
if (port_speed == USB_SPEED_LOW) {
val = pmc_readl(tegra, PMC_SLEEPWALK_REG(port));
val &= ~(UTMIP_AN_B | UTMIP_HIGHZ_B | UTMIP_AN_C |
UTMIP_HIGHZ_C | UTMIP_AN_D | UTMIP_HIGHZ_D);
pmc_writel(tegra, val, PMC_SLEEPWALK_REG(port));
} else {
val = pmc_readl(tegra, PMC_SLEEPWALK_REG(port));
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;
pmc_writel(tegra, val, PMC_SLEEPWALK_REG(port));
}
} else {
/*
* Program walk sequence: pull down both dp and dn lines,
* tristate lines once a hotplug-in wake event is detected
*/
val = pmc_readl(tegra, PMC_SLEEPWALK_REG(port));
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;
pmc_writel(tegra, val, PMC_SLEEPWALK_REG(port));
}
/* Turn on pad detectors */
val = pmc_readl(tegra, PMC_USB_AO);
val &= ~(USBOP_VAL_PD(port) | USBON_VAL_PD(port));
pmc_writel(tegra, val, PMC_USB_AO);
/* Add small delay before usb detectors provide stable line values */
usleep_range(1000, 1100);
/* Program thermally encoded RCTRL_VAL, TCTRL_VAL into PMC space */
if (tegra->utmip_tctrl_val | tegra->utmip_rctrl_val) {
val = pmc_readl(tegra, PMC_UTMIP_TERM_PAD_CFG);
val = PMC_TCTRL_VAL(tegra->utmip_tctrl_val) |
PMC_RCTRL_VAL(tegra->utmip_rctrl_val);
pmc_writel(tegra, val, PMC_UTMIP_TERM_PAD_CFG);
}
/* Turn over pad configuration to PMC for line wake events */
val = pmc_readl(tegra, PMC_SLEEP_CFG);
val &= ~UTMIP_WAKE_VAL(port, ~0);
if (device_may_wakeup(tegra->dev)) {
val |= UTMIP_WAKE_VAL(port, WAKE_VAL_ANY);
} else if (tegra_xhci_port_may_wakeup(tegra->xhci, pad)) {
switch (port_speed) {
case USB_SPEED_LOW:
val |= UTMIP_WAKE_VAL(port, WAKE_VAL_FSJ);
break;
case USB_SPEED_FULL:
case USB_SPEED_HIGH:
val |= UTMIP_WAKE_VAL(port, WAKE_VAL_FSK);
break;
default:
val |= UTMIP_WAKE_VAL(port, WAKE_VAL_NONE);
}
} else {
val |= UTMIP_WAKE_VAL(port, WAKE_VAL_NONE);
}
pmc_writel(tegra, val, PMC_SLEEP_CFG);
val |= UTMIP_RCTRL_USE_PMC(port) | UTMIP_TCTRL_USE_PMC(port);
val |= UTMIP_MASTER_ENABLE(port) | UTMIP_FSLS_USE_PMC(port);
pmc_writel(tegra, val, PMC_SLEEP_CFG);
}
static void utmi_pmc_wake_disable(struct tegra_xusb_phy *tegra,
unsigned int pad)
{
u32 val, port;
port = tegra->soc_config->pmc_portmap[pad];
val = pmc_readl(tegra, PMC_SLEEP_CFG);
if (!(val & UTMIP_MASTER_ENABLE(port))) {
dev_info(tegra->dev, "UTMI wake already disabled on port %d\n",
port);
return;
}
val = pmc_readl(tegra, PMC_SLEEP_CFG);
val &= ~UTMIP_WAKE_VAL(port, 0xF);
val |= UTMIP_WAKE_VAL(port, WAKE_VAL_NONE);
pmc_writel(tegra, val, PMC_SLEEP_CFG);
/* Disable PMC master mode by clearing MASTER_EN */
val = pmc_readl(tegra, PMC_SLEEP_CFG);
val |= UTMIP_RCTRL_USE_PMC(port) | UTMIP_TCTRL_USE_PMC(port);
val &= ~(UTMIP_FSLS_USE_PMC(port) | UTMIP_MASTER_ENABLE(port));
pmc_writel(tegra, val, PMC_SLEEP_CFG);
val = pmc_readl(tegra, PMC_TRIGGERS);
val &= ~UTMIP_CAP_CFG(port);
pmc_writel(tegra, val, PMC_TRIGGERS);
/* turn off pad detectors */
val = pmc_readl(tegra, PMC_USB_AO);
val |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port));
pmc_writel(tegra, val, PMC_USB_AO);
val = pmc_readl(tegra, PMC_TRIGGERS);
val |= UTMIP_CLR_WALK_PTR(port);
val |= UTMIP_CLR_WAKE_ALARM(port);
pmc_writel(tegra, val, PMC_TRIGGERS);
}
static void pmc_wake_enable(struct tegra_xusb_phy *tegra)
{
unsigned long utmi_pads, hsic_pads;
int pad;
hsic_pads = tegra->board_data.hsic_pads;
for_each_set_bit(pad, &hsic_pads, TEGRA_XUSB_HSIC_COUNT)
hsic_pmc_wake_enable(tegra, pad);
utmi_pads = tegra->board_data.utmi_pads;
for_each_set_bit(pad, &utmi_pads, TEGRA_XUSB_UTMI_COUNT)
utmi_pmc_wake_enable(tegra, pad);
}
static void pmc_wake_disable(struct tegra_xusb_phy *tegra)
{
unsigned long utmi_pads, hsic_pads;
int pad;
hsic_pads = tegra->board_data.hsic_pads;
for_each_set_bit(pad, &hsic_pads, TEGRA_XUSB_HSIC_COUNT)
hsic_pmc_wake_disable(tegra, pad);
utmi_pads = tegra->board_data.utmi_pads;
for_each_set_bit(pad, &utmi_pads, TEGRA_XUSB_UTMI_COUNT)
utmi_pmc_wake_disable(tegra, pad);
}
static int tegra_xusb_phy_mbox_notifier(struct notifier_block *nb,
unsigned long event, void *p)
{
struct tegra_xusb_phy *tegra = container_of(nb, struct tegra_xusb_phy,
mbox_nb);
struct mbox_notifier_data *data = (struct mbox_notifier_data *)p;
unsigned long ports;
int i, pad, ret;
switch (event) {
case MBOX_CMD_INC_SSPI_CLOCK:
case MBOX_CMD_DEC_SSPI_CLOCK:
if (tegra->soc_config->use_hs_src_clk2) {
data->resp_cmd = MBOX_CMD_ACK;
data->resp_data = data->msg_data;
return NOTIFY_STOP;
}
ret = ss_set_clock_rate(tegra, data->msg_data * 1000);
data->resp_data = clk_get_rate(tegra->ss_src_clk) / 1000;
if (ret)
data->resp_cmd = MBOX_CMD_NACK;
else
data->resp_cmd = MBOX_CMD_ACK;
return NOTIFY_STOP;
case MBOX_CMD_SAVE_DFE_CTLE_CTX:
data->resp_data = data->msg_data;
if (data->msg_data > TEGRA_XUSB_SS_COUNT) {
data->resp_cmd = MBOX_CMD_NACK;
} else {
ss_save_context(tegra, data->msg_data);
data->resp_cmd = MBOX_CMD_ACK;
}
return NOTIFY_STOP;
case MBOX_CMD_STAR_HSIC_IDLE:
case MBOX_CMD_STOP_HSIC_IDLE:
ports = data->msg_data;
data->resp_data = ports;
for_each_set_bit(i, &ports, BITS_PER_LONG) {
pad = i - 1 - tegra->soc_config->hsic_port_offset;
if (pad > TEGRA_XUSB_HSIC_COUNT) {
ret = -EINVAL;
break;
}
if (event == MBOX_CMD_STAR_HSIC_IDLE)
ret = hsic_pad_set_pupd(tegra, pad, PUPD_IDLE);
else
ret = hsic_pad_set_pupd(tegra, pad,
PUPD_DISABLE);
if (ret)
break;
}
if (ret)
data->resp_cmd = MBOX_CMD_NACK;
else
data->resp_cmd = MBOX_CMD_ACK;
return NOTIFY_STOP;
default:
return NOTIFY_DONE;
}
}
static int tegra_xusb_phy_init(struct usb_phy *phy)
{
struct tegra_xusb_phy *tegra = phy_to_tegra(phy);
unsigned long pads;
int i, ret;
BUG_ON(!tegra->xhci);
/* Register MBOX command handler */
ret = tegra_xhci_register_mbox_notifier(tegra->xhci, &tegra->mbox_nb);
if (ret) {
dev_err(tegra->dev, "Failed to register handler\n");
return ret;
}
/* Enable VBUS and HSIC supplies */
pads = tegra->board_data.utmi_pads;
for_each_set_bit(i, &pads, TEGRA_XUSB_UTMI_COUNT) {
ret = regulator_enable(tegra->utmi_vbus[i]);
if (ret) {
dev_err(tegra->dev, "Failed to enable vbus%d\n", i);
return ret;
}
}
if (tegra->board_data.hsic_pads) {
ret = regulator_enable(tegra->vddio_hsic);
if (ret) {
dev_err(tegra->dev, "Failed to enable vddio-hsic\n");
return ret;
}
}
/* Initialize clocks */
ret = clk_prepare_enable(tegra->ss_clk);
if (ret) {
dev_err(tegra->dev, "Failed to enable xusb_ss clock\n");
return ret;
}
ret = ss_set_clock_rate(tegra, SS_CLK_HIGH_SPEED);
if (ret) {
dev_err(tegra->dev, "Failed to set xusb_ss rate\n");
return ret;
}
/* Initialize pads and map them to XUSB controller */
if (tegra->soc_config->recalc_tctrl_rctrl)
utmi_calc_tctrl_rctrl(tegra);
init_ports(tegra);
/* Release SS wake logic */
ss_set_clamp(tegra, false);
ss_set_vcore(tegra, true);
/* Enable UTMI pads */
utmi_pads_enable(tegra);
/* Pull SS out of reset */
tegra_periph_reset_deassert(tegra->ss_clk);
enable_irq(tegra->padctl_irq);
return 0;
}
static void tegra_xusb_phy_shutdown(struct usb_phy *phy)
{
struct tegra_xusb_phy *tegra = phy_to_tegra(phy);
unsigned long pads;
int i;
BUG_ON(!tegra->xhci);
disable_irq(tegra->padctl_irq);
tegra_xhci_unregister_mbox_notifier(tegra->xhci, &tegra->mbox_nb);
pads = tegra->board_data.hsic_pads;
for_each_set_bit(i, &pads, TEGRA_XUSB_HSIC_COUNT)
hsic_pad_disable(tegra, i);
clk_disable_unprepare(tegra->ss_clk);
pads = tegra->board_data.utmi_pads;
for_each_set_bit(i, &pads, TEGRA_XUSB_UTMI_COUNT)
regulator_disable(tegra->utmi_vbus[i]);
if (tegra->board_data.hsic_pads)
regulator_disable(tegra->vddio_hsic);
}
/*
* Begin suspend of the XUSB PHY. This should be called before the SuperSpeed
* partition has been gated.
*/
void tegra_xusb_phy_presuspend(struct usb_phy *phy)
{
struct tegra_xusb_phy *tegra = phy_to_tegra(phy);
/*
* Set up SS wakeup logic and prepare for SS partition to be
* power-gated.
*/
enable_wake_interrupts(tegra);
ss_set_clamp(tegra, true);
tegra_periph_reset_assert(tegra->ss_clk);
clk_disable_unprepare(tegra->ss_clk);
usleep_range(100, 200);
}
EXPORT_SYMBOL(tegra_xusb_phy_presuspend);
/*
* Finish suspend of the XUSB PHY SuperSpeed partition and set up wake events
* from the PMC. This should be called after the SuperSpeed partition has
* been gated.
*/
static void tegra_xusb_phy_suspend(struct usb_phy *phy)
{
struct tegra_xusb_phy *tegra = phy_to_tegra(phy);
/* Finalize SS partition suspend */
ss_set_vcore(tegra, false);
/* Enable PMC wakeup logic */
utmi_calc_tctrl_rctrl(tegra);
pmc_wake_enable(tegra);
}
/*
* Finish suspend of the XUSB PHY. This should be called after both the host
* and SuperSpeed partitions have been gated.
*/
void tegra_xusb_phy_postsuspend(struct usb_phy *phy)
{
struct tegra_xusb_phy *tegra = phy_to_tegra(phy);
/* Finalize USB 2.0 suspend */
utmi_pads_release(tegra);
utmi_pads_disable(tegra);
}
EXPORT_SYMBOL(tegra_xusb_phy_postsuspend);
/*
* Begin resume of the XUSB PHY. This should be called before the host
* controller has started its resume process.
*/
void tegra_xusb_phy_preresume(struct usb_phy *phy)
{
struct tegra_xusb_phy *tegra = phy_to_tegra(phy);
/* Clear wakeup events */
disable_wake_interrupts(tegra);
/* Re-enable UTMI pads */
utmi_pads_enable(tegra);
if (tegra->soc_config->recalc_tctrl_rctrl)
utmi_calc_tctrl_rctrl(tegra);
/* Re-program ports and assign them back to XUSB */
init_ports(tegra);
}
EXPORT_SYMBOL(tegra_xusb_phy_preresume);
/*
* Resume the XUSB PHY SuperSpeed partition. This should be called after
* the SS partition has been un-gated.
*/
static void tegra_xusb_phy_resume(struct usb_phy *phy)
{
struct tegra_xusb_phy *tegra = phy_to_tegra(phy);
/* Re-initialize SS partition */
clk_prepare_enable(tegra->ss_clk);
ss_set_vcore(tegra, true);
usleep_range(150, 200);
ss_set_clamp(tegra, false);
tegra_periph_reset_deassert(tegra->ss_clk);
ss_set_clock_rate(tegra, SS_CLK_HIGH_SPEED);
}
/*
* Finalize resume for the XUSB PHY. This should be called after the host
* controller has finished loading its firmware.
*/
void tegra_xusb_phy_postresume(struct usb_phy *phy)
{
struct tegra_xusb_phy *tegra = phy_to_tegra(phy);
/* Disable PMC wake control */
pmc_wake_disable(tegra);
}
EXPORT_SYMBOL(tegra_xusb_phy_postresume);
static int tegra_xusb_phy_set_suspend(struct usb_phy *phy, int suspend)
{
if (suspend)
tegra_xusb_phy_suspend(phy);
else
tegra_xusb_phy_resume(phy);
return 0;
}
/*
* Bind a Tegra XHCI host-controller instance with this PHY. This will
* be used later when calling into the Tegra XHCI host driver.
*
* This *must* be called before usb_phy_{init,shutdown,set_suspend}.
*/
void tegra_xusb_phy_bind_xhci_dev(struct usb_phy *phy,
struct tegra_xhci_hcd *xhci)
{
struct tegra_xusb_phy *tegra = phy_to_tegra(phy);
tegra->xhci = xhci;
}
EXPORT_SYMBOL(tegra_xusb_phy_bind_xhci_dev);
static irqreturn_t tegra_xusb_phy_padctl_irq(int irq, void *data)
{
struct tegra_xusb_phy *tegra = (struct tegra_xusb_phy *)data;
const struct tegra_xusb_padctl_regs *padregs;
u32 elpg_program0 = 0;
padregs = tegra->soc_config->padctl_offsets;
elpg_program0 = padctl_readl(tegra, padregs->elpg_program_0);
dev_dbg(tegra->dev, "PADCTL IRQ: elpg_program_0 = 0x%08x\n",
elpg_program0);
/* Clear and disable wake events */
disable_wake_interrupts(tegra);
BUG_ON(!tegra->xhci);
if (elpg_program0 & WAKEUP_EVENT_MASK) {
atomic_notifier_call_chain(&tegra->u_phy.notifier,
USB_EVENT_VBUS, NULL);
} else {
dev_err(tegra->dev, "Unexpected wake event\n");
padctl_writel(tegra, ~0, padregs->elpg_program_0);
}
return IRQ_HANDLED;
}
static const struct tegra_xusb_padctl_regs tegra114_padctl_offsets = {
.boot_media_0 = 0x0,
.usb2_pad_mux_0 = 0x4,
.usb2_port_cap_0 = 0x8,
.snps_oc_map_0 = 0xc,
.usb2_oc_map_0 = 0x10,
.ss_port_map_0 = 0x14,
.oc_det_0 = 0x18,
.elpg_program_0 = 0x1c,
.usb2_bchrg_otgpadX_ctlY_0 = {
{0x20, PADCTL_REG_NONE},
{0x24, PADCTL_REG_NONE},
{PADCTL_REG_NONE, PADCTL_REG_NONE}
},
.usb2_bchrg_bias_pad_0 = 0x28,
.usb2_bchrg_tdcd_dbnc_timer_0 = 0x2c,
.iophy_pll_p0_ctlY_0 = {0x30, 0x34, 0x38, 0x3c},
.iophy_usb3_padX_ctlY_0 = {
{0x40, 0x48, 0x50, 0x58},
{0x44, 0x4c, 0x54, 0x5c}
},
.iophy_misc_pad_pX_ctlY_0 = {
{0x60, 0x68, 0x70, 0x78, 0x80, 0x88},
{0x64, 0x6c, 0x74, 0x7c, 0x84, 0x8c},
{PADCTL_REG_NONE, PADCTL_REG_NONE, PADCTL_REG_NONE,
PADCTL_REG_NONE, PADCTL_REG_NONE},
{PADCTL_REG_NONE, PADCTL_REG_NONE, PADCTL_REG_NONE,
PADCTL_REG_NONE, PADCTL_REG_NONE},
{PADCTL_REG_NONE, PADCTL_REG_NONE, PADCTL_REG_NONE,
PADCTL_REG_NONE, PADCTL_REG_NONE}
},
.usb2_otg_padX_ctlY_0 = {
{0x90, 0x98},
{0x94, 0x9c},
{PADCTL_REG_NONE, PADCTL_REG_NONE}
},
.usb2_bias_pad_ctlY_0 = {0xa0, 0xa4},
.usb2_hsic_padX_ctlY_0 = {
{0xa8, 0xb0, 0xb8},
{0xac, 0xb4, 0xbc}
},
.ulpi_link_trim_ctl0 = 0xc0,
.ulpi_null_clk_trim_ctl0 = 0xc4,
.hsic_strb_trim_ctl0 = 0xc8,
.wake_ctl0 = 0xcc,
.pm_spare0 = 0xd0,
.usb3_pad_mux_0 = PADCTL_REG_NONE,
.iophy_pll_s0_ctlY_0 = {PADCTL_REG_NONE, PADCTL_REG_NONE,
PADCTL_REG_NONE, PADCTL_REG_NONE},
.iophy_misc_pad_s0_ctlY_0 = {PADCTL_REG_NONE, PADCTL_REG_NONE,
PADCTL_REG_NONE, PADCTL_REG_NONE,
PADCTL_REG_NONE, PADCTL_REG_NONE},
};
static const struct tegra_xusb_padctl_regs tegra124_padctl_offsets = {
.boot_media_0 = 0x0,
.usb2_pad_mux_0 = 0x4,
.usb2_port_cap_0 = 0x8,
.snps_oc_map_0 = 0xc,
.usb2_oc_map_0 = 0x10,
.ss_port_map_0 = 0x14,
.oc_det_0 = 0x18,
.elpg_program_0 = 0x1c,
.usb2_bchrg_otgpadX_ctlY_0 = {
{0x20, 0x24},
{0x28, 0x2c},
{0x30, 0x34}
},
.usb2_bchrg_bias_pad_0 = 0x38,
.usb2_bchrg_tdcd_dbnc_timer_0 = 0x3c,
.iophy_pll_p0_ctlY_0 = {0x40, 0x44, 0x48, 0x4c},
.iophy_usb3_padX_ctlY_0 = {
{0x50, 0x58, 0x60, 0x68},
{0x54, 0x5c, 0x64, 0x6c}
},
.iophy_misc_pad_pX_ctlY_0 = {
{0x70, 0x78, 0x80, 0x88, 0x90, 0x98},
{0x74, 0x7c, 0x84, 0x8c, 0x94, 0x9c},
{0xec, 0xf8, 0x104, 0x110, 0x11c, 0x128},
{0xf0, 0xfc, 0x108, 0x114, 0x120, 0x12c},
{0xf4, 0x100, 0x10c, 0x118, 0x124, 0x130}
},
.usb2_otg_padX_ctlY_0 = {
{0xa0, 0xac},
{0xa4, 0xb0},
{0xa8, 0xb4}
},
.usb2_bias_pad_ctlY_0 = {0xb8, 0xbc},
.usb2_hsic_padX_ctlY_0 = {
{0xc0, 0xc8, 0xd0},
{0xc4, 0xcc, 0xd4}
},
.ulpi_link_trim_ctl0 = 0xd8,
.ulpi_null_clk_trim_ctl0 = 0xdc,
.hsic_strb_trim_ctl0 = 0xe0,
.wake_ctl0 = 0xe4,
.pm_spare0 = 0xe8,
.usb3_pad_mux_0 = 0x134,
.iophy_pll_s0_ctlY_0 = {0x138, 0x13c, 0x140, 0x144},
.iophy_misc_pad_s0_ctlY_0 = {0x148, 0x14c, 0x150, 0x154,
0x158, 0x15c},
};
static const struct tegra_xusb_phy_config tegra114_soc_config = {
.shared_ss_lanes = false,
.save_ctle_context = false,
.release_utmi_in_elpg = true,
.use_hs_src_clk2 = true,
.recalc_tctrl_rctrl = true,
.num_utmi_pads = 2,
.pmc_portmap = {
TEGRA_XUSB_UTMIP_PMC_PORT0,
TEGRA_XUSB_UTMIP_PMC_PORT2,
},
.rx_wander = (0x3 << 4),
.rx_eq = (0x3928 << 8),
.cdr_cntl = (0x26 << 24),
.dfe_cntl = 0x002008EE,
.hs_slew = (0xE << 6),
.ls_rslew_pad = {0x3 << 14, 0x0 << 14, 0x0},
.hs_disc_lvl = (0x5 << 2),
.spare_in = 0x0,
.utmi_port_offset = 2,
.hsic_port_offset = 5,
.padctl_offsets = &tegra114_padctl_offsets,
};
static const struct tegra_xusb_phy_config tegra124_soc_config = {
.shared_ss_lanes = true,
.save_ctle_context = true,
.release_utmi_in_elpg = false,
.use_hs_src_clk2 = false,
.recalc_tctrl_rctrl = false,
.num_utmi_pads = 3,
.pmc_portmap = {
TEGRA_XUSB_UTMIP_PMC_PORT0,
TEGRA_XUSB_UTMIP_PMC_PORT1,
TEGRA_XUSB_UTMIP_PMC_PORT2,
},
.rx_wander = (0xF << 4),
.rx_eq = (0xF070 << 8),
.cdr_cntl = (0x26 << 24),
.dfe_cntl = 0x002008EE,
.hs_slew = (0xE << 6),
.ls_rslew_pad = {0x3 << 14, 0x0 << 14, 0x0 << 14},
.hs_disc_lvl = (0x5 << 2),
.spare_in = 0x1,
.utmi_port_offset = 2,
.hsic_port_offset = 6,
.padctl_offsets = &tegra124_padctl_offsets,
};
static struct of_device_id tegra_xusb_phy_id_table[] = {
{
.compatible = "nvidia,tegra114-xusb-phy",
.data = &tegra114_soc_config,
},
{
.compatible = "nvidia,tegra124-xusb-phy",
.data = &tegra124_soc_config,
},
{ },
};
MODULE_DEVICE_TABLE(of, tegra_xusb_phy_id_table);
static int tegra_xusb_phy_parse_dt(struct tegra_xusb_phy *tegra)
{
struct tegra_xusb_phy_board_data *bdata = &tegra->board_data;
struct device_node *np = tegra->dev->of_node;
u32 val;
int ret, i;
if (of_property_read_u32(np, "nvidia,ss-pads", &val)) {
dev_err(tegra->dev, "Missing SS pad map\n");
return -EINVAL;
}
bdata->ss_pads = val;
if (of_property_read_u32(np, "nvidia,hsic-pads", &val)) {
dev_err(tegra->dev, "Missing HSIC pad map\n");
return -EINVAL;
}
bdata->hsic_pads = val;
for_each_set_bit(i, &bdata->hsic_pads, TEGRA_XUSB_HSIC_COUNT) {
char prop[sizeof("nvidia,hsicN-config")];
sprintf(prop, "nvidia,hsic%d-config", i);
ret = of_property_read_u8_array(np, prop,
(u8 *)&bdata->hsic[i],
sizeof(bdata->hsic[i]));
if (ret) {
dev_err(tegra->dev, "Missing hsic %d config\n", i);
return -EINVAL;
}
}
if (of_property_read_u32(np, "nvidia,utmi-pads", &val)) {
dev_err(tegra->dev, "Missing UTMI pad map\n");
return -EINVAL;
}
bdata->utmi_pads = val;
if (of_property_read_u32(np, "nvidia,ss-portmap", &bdata->ss_portmap)) {
dev_err(tegra->dev, "Missing SS portmap\n");
return -EINVAL;
}
if (of_property_read_u32(np, "nvidia,lane-owner", &bdata->lane_owner)) {
dev_err(tegra->dev, "Missing lane owner\n");
return -EINVAL;
}
of_property_read_u32(np, "nvidia,xusb-hs-xcvr-setup-offset",
&bdata->hs_xcvr_setup_offset);
return 0;
}
static void tegra_xusb_phy_read_calib_data(struct tegra_xusb_phy *tegra)
{
u32 usb_calib0;
usb_calib0 = tegra30_fuse_readl(FUSE_SKU_USB_CALIB_0);
dev_dbg(tegra->dev, "usb_calib0 = 0x%08x\n", usb_calib0);
/*
* Read calibration data from fuse:
* set HS_CURR_LEVEL (PAD0) = usb_calib0[5:0]
* set TERM_RANGE_ADJ = usb_calib0[10:7]
* set HS_SQUELCH_LEVEL = usb_calib0[12:11]
* set HS_IREF_CAP = usb_calib0[14:13]
* set HS_CURR_LEVEL (PAD1/2) = usb_calib0[20:15]
*/
tegra->calib_data.hs_curr_level_pad[0] = (usb_calib0 >> 0) & 0x3f;
tegra->calib_data.hs_term_range_adj = (usb_calib0 >> 7) & 0xf;
tegra->calib_data.hs_squelch_level = (usb_calib0 >> 11) & 0x3;
tegra->calib_data.hs_iref_cap = (usb_calib0 >> 13) & 0x3;
tegra->calib_data.hs_curr_level_pad[1] = (usb_calib0 >> 15) & 0x3f;
tegra->calib_data.hs_curr_level_pad[2] = (usb_calib0 >> 15) & 0x3f;
}
static int tegra_xusb_phy_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct resource *res;
struct tegra_xusb_phy *tegra;
unsigned long utmi_pads;
int err, i;
tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
if (!tegra)
return -ENOMEM;
tegra->dev = &pdev->dev;
platform_set_drvdata(pdev, tegra);
tegra->mbox_nb.notifier_call = tegra_xusb_phy_mbox_notifier;
match = of_match_device(tegra_xusb_phy_id_table, &pdev->dev);
if (!match) {
dev_err(&pdev->dev, "No matching device found\n");
return -ENODEV;
}
tegra->soc_config = match->data;
tegra_xusb_phy_read_calib_data(tegra);
err = tegra_xusb_phy_parse_dt(tegra);
if (err)
return err;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "Failed to get padctl regs\n");
return -ENODEV;
}
tegra->padctl_regs = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (!tegra->padctl_regs) {
dev_err(&pdev->dev, "Failed to map padctl regs\n");
return -ENOMEM;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res) {
dev_err(&pdev->dev, "Failed to get UTMI pad regs\n");
return -ENODEV;
}
tegra->pad_regs = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (!tegra->pad_regs) {
dev_err(&pdev->dev, "Failed to map UTMI pad regs\n");
return -ENOMEM;
}
tegra->pmc_regs = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"nvidia,pmc");
if (IS_ERR(tegra->pmc_regs)) {
dev_err(&pdev->dev, "Failed to get PMC regs\n");
return PTR_ERR(tegra->pmc_regs);
}
tegra->clkrst_regs = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"nvidia,clkrst");
if (IS_ERR(tegra->clkrst_regs)) {
dev_err(&pdev->dev, "Failed to get CLKRST regs\n");
return PTR_ERR(tegra->clkrst_regs);
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(&pdev->dev, "Failed to get padctl IRQ\n");
return -ENODEV;
}
tegra->padctl_irq = res->start;
err = devm_request_irq(&pdev->dev, tegra->padctl_irq,
tegra_xusb_phy_padctl_irq, IRQF_TRIGGER_HIGH,
dev_name(&pdev->dev), tegra);
if (err != 0) {
dev_err(&pdev->dev, "Failed to request padctl IRQ\n");
return err;
}
disable_irq(tegra->padctl_irq);
if (tegra->board_data.hsic_pads) {
tegra->vddio_hsic = devm_regulator_get(&pdev->dev,
"vddio-hsic");
if (IS_ERR(tegra->vddio_hsic)) {
dev_err(&pdev->dev,
"Failed to get vddio-hsic regulator\n");
return PTR_ERR(tegra->vddio_hsic);
}
}
utmi_pads = tegra->board_data.utmi_pads;
for_each_set_bit(i, &utmi_pads, TEGRA_XUSB_UTMI_COUNT) {
char reg_name[sizeof("vbusN")];
sprintf(reg_name, "vbus%d", i + 1);
tegra->utmi_vbus[i] = devm_regulator_get(&pdev->dev, reg_name);
if (IS_ERR(tegra->utmi_vbus[i])) {
dev_err(&pdev->dev, "Failed to get %s regulator\n",
reg_name);
return PTR_ERR(tegra->utmi_vbus[i]);
}
}
tegra->ss_src_clk = devm_clk_get(&pdev->dev, "xusb_ss_src");
if (IS_ERR(tegra->ss_src_clk)) {
dev_err(&pdev->dev, "Failed to get SS source clock\n");
return PTR_ERR(tegra->ss_src_clk);
}
tegra->ss_clk = devm_clk_get(&pdev->dev, "xusb_ss");
if (IS_ERR(tegra->ss_clk)) {
dev_err(&pdev->dev, "Failed to get SS clock\n");
return PTR_ERR(tegra->ss_clk);
}
tegra->pll_u_480M = devm_clk_get(&pdev->dev, "pll_u_480M");
if (IS_ERR(tegra->pll_u_480M)) {
dev_err(&pdev->dev, "Failed to get PLL_U_480M\n");
return PTR_ERR(tegra->pll_u_480M);
}
tegra->clk_m = devm_clk_get(&pdev->dev, "clk_m");
if (IS_ERR(tegra->clk_m)) {
dev_err(&pdev->dev, "Failed to get clk_m\n");
return PTR_ERR(tegra->clk_m);
}
tegra->pad_clk = devm_clk_get(&pdev->dev, "pad_clk");
if (IS_ERR(tegra->pad_clk)) {
dev_err(&pdev->dev, "Failed to get UTMI pad clock\n");
return PTR_ERR(tegra->pad_clk);
}
tegra->plle = devm_clk_get(&pdev->dev, "pll_e");
if (IS_ERR(tegra->plle)) {
dev_err(&pdev->dev, "Failed to get PLLE\n");
return PTR_ERR(tegra->plle);
}
err = clk_prepare_enable(tegra->plle);
if (err) {
dev_err(&pdev->dev, "Failed to enable PLLE\n");
return err;
}
tegra->u_phy.dev = &pdev->dev;
tegra->u_phy.init = tegra_xusb_phy_init;
tegra->u_phy.shutdown = tegra_xusb_phy_shutdown;
tegra->u_phy.set_suspend = tegra_xusb_phy_set_suspend;
ATOMIC_INIT_NOTIFIER_HEAD(&tegra->u_phy.notifier);
err = usb_add_phy_dev(&tegra->u_phy);
if (err < 0) {
dev_err(&pdev->dev, "Failed to add PHY device\n");
clk_disable_unprepare(tegra->plle);
return err;
}
return 0;
}
static int tegra_xusb_phy_remove(struct platform_device *pdev)
{
struct tegra_xusb_phy *tegra = platform_get_drvdata(pdev);
usb_remove_phy(&tegra->u_phy);
clk_disable_unprepare(tegra->plle);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int tegra_xusb_phy_pm_suspend(struct device *dev)
{
struct tegra_xusb_phy *tegra = dev_get_drvdata(dev);
clk_disable_unprepare(tegra->plle);
return 0;
}
static int tegra_xusb_phy_pm_resume(struct device *dev)
{
struct tegra_xusb_phy *tegra = dev_get_drvdata(dev);
clk_prepare_enable(tegra->plle);
return 0;
}
#endif
static const struct dev_pm_ops tegra_xusb_phy_pm_ops = {
#ifdef CONFIG_PM_SLEEP
.suspend_late = tegra_xusb_phy_pm_suspend,
.resume_early = tegra_xusb_phy_pm_resume,
#endif
};
static struct platform_driver tegra_xusb_phy_driver = {
.probe = tegra_xusb_phy_probe,
.remove = tegra_xusb_phy_remove,
.driver = {
.name = "tegra-xusb-phy",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(tegra_xusb_phy_id_table),
.pm = &tegra_xusb_phy_pm_ops,
},
};
module_platform_driver(tegra_xusb_phy_driver);
MODULE_DESCRIPTION("Tegra XUSB PHY driver");
MODULE_LICENSE("GPL v2");