/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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");