/* * Copyright (c) 2017 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Author: Sung-Hyun Na * * Chip Abstraction Layer for USB PHY * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * 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 "phy-samsung-usb-cal.h" #include "phy-exynos-usb3p1.h" #include "phy-exynos-usb3p1-reg.h" static void exynos_cal_usbphy_q_ch(void *regs_base, u8 enable) { u32 phy_resume; if (enable) { /* WA for Q-channel: disable all q-act from usb */ phy_resume = readl(regs_base + EXYNOS_USBCON_LINK_CTRL); phy_resume |= LINKCTRL_DIS_QACT_ID0; phy_resume |= LINKCTRL_DIS_QACT_VBUS_VALID; phy_resume |= LINKCTRL_DIS_QACT_BVALID; phy_resume |= LINKCTRL_DIS_QACT_LINKGATE; phy_resume &= ~LINKCTRL_FORCE_QACT; udelay(500); writel(phy_resume, regs_base + EXYNOS_USBCON_LINK_CTRL); udelay(500); phy_resume = readl(regs_base + EXYNOS_USBCON_LINK_CTRL); phy_resume |= LINKCTRL_FORCE_QACT; udelay(500); writel(phy_resume, regs_base + EXYNOS_USBCON_LINK_CTRL); } else { phy_resume = readl(regs_base + EXYNOS_USBCON_LINK_CTRL); phy_resume &= ~LINKCTRL_FORCE_QACT; phy_resume |= LINKCTRL_DIS_QACT_ID0; phy_resume |= LINKCTRL_DIS_QACT_VBUS_VALID; phy_resume |= LINKCTRL_DIS_QACT_BVALID; phy_resume |= LINKCTRL_DIS_QACT_LINKGATE; writel(phy_resume, regs_base + EXYNOS_USBCON_LINK_CTRL); } } static void link_vbus_filter_en(struct exynos_usbphy_info *info, u8 enable) { u32 phy_resume; phy_resume = readl(info->regs_base + EXYNOS_USBCON_LINK_CTRL); if (enable) phy_resume &= ~LINKCTRL_BUS_FILTER_BYPASS_MASK; else phy_resume |= LINKCTRL_BUS_FILTER_BYPASS(0xf); writel(phy_resume, info->regs_base + EXYNOS_USBCON_LINK_CTRL); } static void phy_power_en(struct exynos_usbphy_info *info, u8 en) { u32 reg; int main_version; main_version = info->version & EXYNOS_USBCON_VER_MAJOR_VER_MASK; if (main_version == EXYNOS_USBCON_VER_05_0_0) { void *__iomem reg_base; if (info->used_phy_port == 1) reg_base = info->regs_base_2nd; else reg_base = info->regs_base; /* 3.0 PHY Power Down control */ reg = readl(reg_base + EXYNOS_USBCON_PWR); if (en) reg &= ~(PWR_TEST_POWERDOWN_SSP); else reg |= (PWR_TEST_POWERDOWN_SSP); writel(reg, reg_base + EXYNOS_USBCON_PWR); } else if (main_version == EXYNOS_USBCON_VER_03_0_0) { /* 2.0 PHY Power Down Control */ reg = readl(info->regs_base + EXYNOS_USBCON_HSP_TEST); if (en) reg &= ~HSP_TEST_SIDDQ; else reg |= HSP_TEST_SIDDQ; writel(reg, info->regs_base + EXYNOS_USBCON_HSP_TEST); } } static void phy_sw_rst_high(struct exynos_usbphy_info *info) { void __iomem *regs_base = info->regs_base; int main_version; u32 clkrst; main_version = info->version & EXYNOS_USBCON_VER_MAJOR_VER_MASK; if ((main_version == EXYNOS_USBCON_VER_05_0_0) && (info->used_phy_port == 1)) regs_base = info->regs_base_2nd; clkrst = readl(regs_base + EXYNOS_USBCON_CLKRST); clkrst |= CLKRST_PHY_SW_RST; clkrst |= CLKRST_PHY_RST_SEL; writel(clkrst, regs_base + EXYNOS_USBCON_CLKRST); } static void phy_sw_rst_low(struct exynos_usbphy_info *info) { void __iomem *regs_base = info->regs_base; int main_version; u32 clkrst; main_version = info->version & EXYNOS_USBCON_VER_MAJOR_VER_MASK; if ((main_version == EXYNOS_USBCON_VER_05_0_0) && (info->used_phy_port == 1)) regs_base = info->regs_base_2nd; clkrst = readl(regs_base + EXYNOS_USBCON_CLKRST); clkrst |= CLKRST_PHY_RST_SEL; clkrst &= ~CLKRST_PHY_SW_RST; clkrst &= ~CLKRST_PORT_RST; writel(clkrst, regs_base + EXYNOS_USBCON_CLKRST); } void phy_exynos_usb_v3p1_pma_ready(struct exynos_usbphy_info *info) { void __iomem *regs_base = info->regs_base; u32 reg; reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL); reg &= ~PMA_LOW_PWRN; writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL); udelay(1); reg |= PMA_APB_SW_RST; reg |= PMA_INIT_SW_RST; reg |= PMA_CMN_SW_RST; reg |= PMA_TRSV_SW_RST; writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL); udelay(1); reg &= ~PMA_APB_SW_RST; reg &= ~PMA_INIT_SW_RST; reg &= ~PMA_PLL_REF_REQ_MASK; reg &= ~PMA_REF_FREQ_MASK; reg |= PMA_REF_FREQ_SET(0x1); writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL); reg = readl(regs_base + EXYNOS_USBCON_LINK_CTRL); reg &= ~LINKCTRL_PIPE3_FORCE_EN; writel(reg, regs_base + EXYNOS_USBCON_LINK_CTRL); } void phy_exynos_usb_v3p1_pma_sw_rst_release(struct exynos_usbphy_info *info) { void __iomem *regs_base = info->regs_base; u32 reg; /* Reset Release for USB/DP PHY */ reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL); reg &= ~PMA_CMN_SW_RST; reg &= ~PMA_TRSV_SW_RST; writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL); udelay(200); /* Change pipe pclk to pipe3 */ reg = readl(regs_base + EXYNOS_USBCON_CLKRST); reg |= CLKRST_LINK_PCLK_SEL; writel(reg, regs_base + EXYNOS_USBCON_CLKRST); } void phy_exynos_usb_v3p1_pipe_ready(struct exynos_usbphy_info *info) { void __iomem *regs_base = info->regs_base; u32 reg; reg = readl(regs_base + EXYNOS_USBCON_LINK_CTRL); reg &= ~LINKCTRL_PIPE3_FORCE_EN; writel(reg, regs_base + EXYNOS_USBCON_LINK_CTRL); } void phy_exynos_usb_v3p1_pipe_ovrd(struct exynos_usbphy_info *info) { void __iomem *regs_base = info->regs_base; u32 reg; /* PMA Disable and force pipe3 signal for link */ reg = readl(regs_base + EXYNOS_USBCON_LINK_CTRL); reg |= LINKCTRL_PIPE3_FORCE_EN; reg &= ~LINKCTRL_PIPE3_FORCE_PHY_STATUS; reg |= LINKCTRL_PIPE3_FORCE_RX_ELEC_IDLE; writel(reg, regs_base + EXYNOS_USBCON_LINK_CTRL); /* PMA Disable */ reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL); reg |= PMA_LOW_PWRN; writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL); } void phy_exynos_usb_v3p1_enable(struct exynos_usbphy_info *info) { void __iomem *regs_base = info->regs_base; u32 reg; u32 reg_hsp; bool ss_only_cap; int main_version; main_version = info->version & EXYNOS_USBCON_VER_MAJOR_VER_MASK; ss_only_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 4; if (main_version == EXYNOS_USBCON_VER_03_0_0) { #if !defined(CONFIG_BOARD_ZEBU) /* Set force q-channel */ exynos_cal_usbphy_q_ch(regs_base, 1); #endif /* Link Reset */ if (main_version == EXYNOS_USBCON_VER_03_0_0) { reg = readl(info->regs_base + EXYNOS_USBCON_CLKRST); reg |= CLKRST_LINK_SW_RST; writel(reg, regs_base + EXYNOS_USBCON_CLKRST); udelay(10); reg &= ~CLKRST_LINK_SW_RST; writel(reg, regs_base + EXYNOS_USBCON_CLKRST); } } /* Set PHY Reset High */ phy_sw_rst_high(info); /* Enable PHY Power Mode */ phy_power_en(info, 1); if (!ss_only_cap) { reg = readl(regs_base + EXYNOS_USBCON_UTMI); reg &= ~UTMI_FORCE_SUSPEND; reg &= ~UTMI_FORCE_SLEEP; reg &= ~UTMI_DP_PULLDOWN; reg &= ~UTMI_DM_PULLDOWN; writel(reg, regs_base + EXYNOS_USBCON_UTMI); /* set phy clock & control HS phy */ reg = readl(regs_base + EXYNOS_USBCON_HSP); if (info->common_block_disable) { reg |= HSP_EN_UTMISUSPEND; reg |= HSP_COMMONONN; } else { reg &= ~HSP_COMMONONN; } writel(reg, regs_base + EXYNOS_USBCON_HSP); } else { void *ss_reg_base; if (info->used_phy_port == 1) ss_reg_base = info->regs_base_2nd; else ss_reg_base = info->regs_base; reg = readl(ss_reg_base + EXYNOS_USBCON_CLKRST); reg |= CLKRST_LINK_PCLK_SEL; writel(reg, ss_reg_base + EXYNOS_USBCON_CLKRST); } udelay(100); /* Set PHY Reset Low */ phy_sw_rst_low(info); if (ss_only_cap) { phy_exynos_usb_v3p1_late_enable(info); return; } /* Select PHY MUX */ if (info->dual_phy) { u32 physel; physel = readl(regs_base + EXYNOS_USBCON_DUALPHYSEL); if (info->used_phy_port == 0) { physel &= ~DUALPHYSEL_PHYSEL_CTRL; physel &= ~DUALPHYSEL_PHYSEL_SSPHY; physel &= ~DUALPHYSEL_PHYSEL_PIPECLK; physel &= ~DUALPHYSEL_PHYSEL_PIPERST; } else { physel |= DUALPHYSEL_PHYSEL_CTRL; physel |= DUALPHYSEL_PHYSEL_SSPHY; physel |= DUALPHYSEL_PHYSEL_PIPECLK; physel |= DUALPHYSEL_PHYSEL_PIPERST; } writel(physel, regs_base + EXYNOS_USBCON_DUALPHYSEL); } /* Follow setting sequence for USB Link */ /* * 1. Set VBUS Valid and DP-Pull up control * by VBUS pad usage */ reg = readl(regs_base + EXYNOS_USBCON_UTMI); reg_hsp = readl(regs_base + EXYNOS_USBCON_HSP); if (info->not_used_vbus_pad) { link_vbus_filter_en(info, false); reg |= UTMI_FORCE_BVALID; reg |= UTMI_FORCE_VBUSVALID; reg_hsp |= HSP_VBUSVLDEXTSEL; reg_hsp |= HSP_VBUSVLDEXT; } else { link_vbus_filter_en(info, true); reg &= ~UTMI_FORCE_BVALID; reg &= ~UTMI_FORCE_VBUSVALID; reg_hsp &= ~HSP_VBUSVLDEXTSEL; reg_hsp &= ~HSP_VBUSVLDEXT; } writel(reg, regs_base + EXYNOS_USBCON_UTMI); writel(reg_hsp, regs_base + EXYNOS_USBCON_HSP); /* 2. OVC io usage */ reg = readl(regs_base + EXYNOS_USBCON_LINK_PORT); if (info->use_io_for_ovc) { reg &= ~LINKPORT_HUB_PORT_SEL_OCD_U3; reg &= ~LINKPORT_HUB_PORT_SEL_OCD_U2; } else { reg |= LINKPORT_HUB_PORT_SEL_OCD_U3; reg |= LINKPORT_HUB_PORT_SEL_OCD_U2; } writel(reg, regs_base + EXYNOS_USBCON_LINK_PORT); /* Enable ReWA */ if (info->hs_rewa) phy_exynos_usb3p1_rewa_ready(info); } enum exynos_usbcon_cr { USBCON_CR_ADDR = 0, USBCON_CR_DATA = 1, USBCON_CR_READ = 18, USBCON_CR_WRITE = 19, }; static u16 phy_exynos_usb_v3p1_cr_access(struct exynos_usbphy_info *info, enum exynos_usbcon_cr cr_bit, u16 data) { void __iomem *base; u32 ssp_crctl0; u32 ssp_crctl1; u32 loop; u32 loop_cnt; if (info->used_phy_port != -1) { if (info->used_phy_port == 0) base = info->regs_base; else base = info->regs_base_2nd; } else base = info->regs_base; /*Clear CR port register*/ ssp_crctl0 = readl(base + EXYNOS_USBCON_SSP_CRCTL0); ssp_crctl0 &= ~(0xf); ssp_crctl0 &= ~(0xffff << 16); writel(ssp_crctl0, base + EXYNOS_USBCON_SSP_CRCTL0); /*Set Data for cr port*/ ssp_crctl0 &= ~SSP_CCTRL0_CR_DATA_IN_MASK; ssp_crctl0 |= SSP_CCTRL0_CR_DATA_IN(data); writel(ssp_crctl0, base + EXYNOS_USBCON_SSP_CRCTL0); if (cr_bit == USBCON_CR_ADDR) loop = 1; else loop = 2; for (loop_cnt = 0; loop_cnt < loop; loop_cnt++) { u32 trigger_bit = 0; u32 handshake_cnt = 2; if (cr_bit == USBCON_CR_ADDR) trigger_bit = SSP_CRCTRL0_CR_CAP_ADDR; else { if (loop_cnt == 0) trigger_bit = SSP_CRCTRL0_CR_CAP_DATA; else { if (cr_bit == USBCON_CR_READ) trigger_bit = SSP_CRCTRL0_CR_READ; else trigger_bit = SSP_CRCTRL0_CR_WRITE; } } /* Handshake Procedure */ do { u32 usec = 100; if (handshake_cnt == 2) ssp_crctl0 |= trigger_bit; else ssp_crctl0 &= ~trigger_bit; writel(ssp_crctl0, base + EXYNOS_USBCON_SSP_CRCTL0); /* Handshake */ do { ssp_crctl1 = readl(base + EXYNOS_USBCON_SSP_CRCTL1); if ((handshake_cnt == 2) && (ssp_crctl1 & SSP_CRCTL1_CR_ACK)) break; else if ((handshake_cnt == 1) && !(ssp_crctl1 & SSP_CRCTL1_CR_ACK)) break; udelay(1); } while (usec-- > 0); if (!usec) pr_err("CRPORT handshake timeout1 (0x%08x)\n", ssp_crctl0); udelay(5); handshake_cnt--; } while (handshake_cnt != 0); udelay(50); } return (u16) ((ssp_crctl1 & SSP_CRCTL1_CR_DATA_OUT_MASK) >> 16); } void phy_exynos_usb_v3p1_cal_cr_write(struct exynos_usbphy_info *info, u16 addr, u16 data) { phy_exynos_usb_v3p1_cr_access(info, USBCON_CR_ADDR, addr); phy_exynos_usb_v3p1_cr_access(info, USBCON_CR_WRITE, data); } u16 phy_exynos_usb_v3p1_cal_cr_read(struct exynos_usbphy_info *info, u16 addr) { phy_exynos_usb_v3p1_cr_access(info, USBCON_CR_ADDR, addr); return phy_exynos_usb_v3p1_cr_access(info, USBCON_CR_WRITE, 0); } void phy_exynos_usb_v3p1_cal_usb3phy_tune_fix_rxeq(struct exynos_usbphy_info *info) { u16 reg; struct exynos_usbphy_ss_tune *tune = info->ss_tune; if (!tune) return; reg = phy_exynos_usb_v3p1_cal_cr_read(info, 0x1006); reg &= ~(1 << 6); phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg); udelay(10); reg |= (1 << 7); phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg); udelay(10); reg &= ~(0x7 << 0x8); reg |= (tune->fix_rxeq_value << 8); phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg); udelay(10); reg |= (1 << 11); phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg); dev_dbg(info->dev, "Reg RX_OVRD_IN_HI : 0x%x\n", phy_exynos_usb_v3p1_cal_cr_read(info, 0x1006)); dev_dbg(info->dev, "Reg RX_CDR_CDR_FSM_DEBUG : 0x%x\n", phy_exynos_usb_v3p1_cal_cr_read(info, 0x101c)); } static void set_ss_tx_impedance(struct exynos_usbphy_info *info) { u16 rtune_debug, tx_ovrd_in_hi; u8 tx_imp; /* obtain calibration code for 45Ohm impedance */ rtune_debug = phy_exynos_usb_v3p1_cal_cr_read(info, 0x3); /* set SUP.DIG.RTUNE_DEBUG.TYPE = 2 */ rtune_debug &= ~(0x3 << 3); rtune_debug |= (0x2 << 3); phy_exynos_usb_v3p1_cal_cr_write(info, 0x3, rtune_debug); /* read SUP.DIG.RTUNE_STAT (0x0004[9:0]) */ tx_imp = phy_exynos_usb_v3p1_cal_cr_read(info, 0x4); /* current_tx_cal_code[9:0] = SUP.DIG.RTUNE_STAT (0x0004[9:0]) */ tx_imp += 8; /* * tx_cal_code[9:0] = current_tx_cal_code[9:0] + 8(decimal) * NOTE, max value is 63; * i.e. if tx_cal_code[9:0] > 63, tx_cal_code[9:0]==63; */ if (tx_imp > 63) tx_imp = 63; /* set LANEX_DIG_TX_OVRD_IN_HI.TX_RESET_OVRD = 1 */ tx_ovrd_in_hi = phy_exynos_usb_v3p1_cal_cr_read(info, 0x1001); tx_ovrd_in_hi |= (1 << 7); phy_exynos_usb_v3p1_cal_cr_write(info, 0x1001, tx_ovrd_in_hi); /* SUP.DIG.RTUNE_DEBUG.MAN_TUNE = 0 */ rtune_debug &= ~(1 << 1); phy_exynos_usb_v3p1_cal_cr_write(info, 0x3, rtune_debug); /* set SUP.DIG.RTUNE_DEBUG.VALUE = tx_cal_code[9:0] */ rtune_debug &= ~(0x1ff << 5); rtune_debug |= (tx_imp << 5); phy_exynos_usb_v3p1_cal_cr_write(info, 0x3, rtune_debug); /* set SUP.DIG.RTUNE_DEBUG.SET_VAL = 1 */ rtune_debug |= (1 << 2); phy_exynos_usb_v3p1_cal_cr_write(info, 0x3, rtune_debug); /* set SUP.DIG.RTUNE_DEBUG.SET_VAL = 0 */ rtune_debug &= ~(1 << 2); phy_exynos_usb_v3p1_cal_cr_write(info, 0x3, rtune_debug); /* set LANEX_DIG_TX_OVRD_IN_HI.TX_RESET = 1 */ tx_ovrd_in_hi |= (1 << 6); phy_exynos_usb_v3p1_cal_cr_write(info, 0x1001, tx_ovrd_in_hi); /* set LANEX_DIG_TX_OVRD_IN_HI.TX_RESET = 0 */ tx_ovrd_in_hi &= ~(1 << 6); phy_exynos_usb_v3p1_cal_cr_write(info, 0x1001, tx_ovrd_in_hi); } void phy_exynos_usb_v3p1_cal_usb3phy_tune_adaptive_eq( struct exynos_usbphy_info *info, u8 eq_fix) { u16 reg; reg = phy_exynos_usb_v3p1_cal_cr_read(info, 0x1006); if (eq_fix) { reg |= (1 << 6); reg &= ~(1 << 7); } else { reg &= ~(1 << 6); reg |= (1 << 7); } phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg); } void phy_exynos_usb_v3p1_usb3phy_tune_chg_rxeq( struct exynos_usbphy_info *info, u8 eq_val) { u16 reg; reg = phy_exynos_usb_v3p1_cal_cr_read(info, 0x1006); reg &= ~(0x7 << 0x8); reg |= ((eq_val & 0x7) << 8); reg |= (1 << 11); phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg); } void phy_exynos_usb_v3p1_late_enable(struct exynos_usbphy_info *info) { u32 version = info->version; if (version > EXYNOS_USBCON_VER_05_0_0) { struct exynos_usbphy_ss_tune *tune = info->ss_tune; /*Set RXDET_MEAS_TIME[11:4] each reference clock*/ phy_exynos_usb_v3p1_cal_cr_write(info, 0x1010, 0x80); if (tune) { phy_exynos_usb_v3p1_cal_cr_write(info, 0x1002, 0x4000 | (tune->tx_deemphasis_3p5db << 7) | tune->tx_swing_full); if (tune->rx_decode_mode) phy_exynos_usb_v3p1_cal_cr_write(info, 0x1026, 0x1); if (tune->set_crport_level_en) { /* * Enable override los_bias, los_level and * tx_vboost_lvl, Set los_bias to 0x5 and * los_level to 0x9 */ phy_exynos_usb_v3p1_cal_cr_write(info, 0x15, 0xA409); /* Set TX_VBOOST_LEVLE to tune->tx_boost_level */ phy_exynos_usb_v3p1_cal_cr_write(info, 0x12, tune->tx_boost_level<<13); } /* to set the charge pump proportional current */ if (tune->set_crport_mpll_charge_pump) phy_exynos_usb_v3p1_cal_cr_write(info, 0x30, 0xC0); if (tune->enable_fixed_rxeq_mode) phy_exynos_usb_v3p1_cal_usb3phy_tune_fix_rxeq(info); if (tune->decrease_ss_tx_imp) set_ss_tx_impedance(info); } } } void phy_exynos_usb_v3p1_disable(struct exynos_usbphy_info *info) { u32 reg; void __iomem *regs_base = info->regs_base; /* set phy clock & control HS phy */ reg = readl(regs_base + EXYNOS_USBCON_UTMI); reg |= UTMI_FORCE_SUSPEND; reg |= UTMI_FORCE_SLEEP; writel(reg, regs_base + EXYNOS_USBCON_UTMI); /* Disable PHY Power Mode */ phy_power_en(info, 0); /* clear force q-channel */ exynos_cal_usbphy_q_ch(regs_base, 0); } void phy_exynos_usb_v3p1_config_host_mode(struct exynos_usbphy_info *info) { u32 reg; void __iomem *regs_base = info->regs_base; /* DP/DM Pull Down Control */ reg = readl(regs_base + EXYNOS_USBCON_UTMI); reg |= UTMI_DP_PULLDOWN; reg |= UTMI_DM_PULLDOWN; reg &= ~UTMI_FORCE_BVALID; reg &= ~UTMI_FORCE_VBUSVALID; writel(reg, regs_base + EXYNOS_USBCON_UTMI); /* Disable Pull-up Register */ reg = readl(regs_base + EXYNOS_USBCON_HSP); reg &= ~HSP_VBUSVLDEXTSEL; reg &= ~HSP_VBUSVLDEXT; writel(reg, regs_base + EXYNOS_USBCON_HSP); } void phy_exynos_usb_v3p1_tune(struct exynos_usbphy_info *info) { u32 hsp_tune, ssp_tune0, ssp_tune1, cnt; bool ss_only_cap; ss_only_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 4; if (!info->tune_param) return; if (!ss_only_cap) { /* hsphy tuning */ void __iomem *regs_base = info->regs_base; hsp_tune = readl(regs_base + EXYNOS_USBCON_HSP_TUNE); cnt = 0; for (; info->tune_param[cnt].value != EXYNOS_USB_TUNE_LAST; cnt++) { char *para_name; int val; val = info->tune_param[cnt].value; if (val == -1) continue; para_name = info->tune_param[cnt].name; /* HSP PARACON : 0x135d_0000 + 0x58 */ if (!strcmp(para_name, "compdis")) { hsp_tune &= ~HSP_TUNE_COMPDIS_MASK; hsp_tune |= HSP_TUNE_COMPDIS_SET(val); } else if (!strcmp(para_name, "otg")) { hsp_tune &= ~HSP_TUNE_OTG_MASK; hsp_tune |= HSP_TUNE_OTG_SET(val); } else if (!strcmp(para_name, "rx_sqrx")) { hsp_tune &= ~HSP_TUNE_SQRX_MASK; hsp_tune |= HSP_TUNE_SQRX_SET(val); } else if (!strcmp(para_name, "tx_fsls")) { hsp_tune &= ~HSP_TUNE_TXFSLS_MASK; hsp_tune |= HSP_TUNE_TXFSLS_SET(val); } else if (!strcmp(para_name, "tx_hsxv")) { hsp_tune &= ~HSP_TUNE_HSXV_MASK; hsp_tune |= HSP_TUNE_HSXV_SET(val); } else if (!strcmp(para_name, "tx_pre_emp")) { hsp_tune &= ~HSP_TUNE_TXPREEMPA_MASK; hsp_tune |= HSP_TUNE_TXPREEMPA_SET(val); } else if (!strcmp(para_name, "tx_pre_emp_plus")) { if (val) hsp_tune |= HSP_TUNE_TXPREEMPA_PLUS; else hsp_tune &= ~HSP_TUNE_TXPREEMPA_PLUS; } else if (!strcmp(para_name, "tx_res")) { hsp_tune &= ~HSP_TUNE_TXRES_MASK; hsp_tune |= HSP_TUNE_TXRES_SET(val); } else if (!strcmp(para_name, "tx_rise")) { hsp_tune &= ~HSP_TUNE_TXRISE_MASK; hsp_tune |= HSP_TUNE_TXRISE_SET(val); } else if (!strcmp(para_name, "tx_vref")) { hsp_tune &= ~HSP_TUNE_TXVREF_MASK; hsp_tune |= HSP_TUNE_TXVREF_SET(val); } } writel(hsp_tune, regs_base + EXYNOS_USBCON_HSP_TUNE); } else { /* ssphy tuning */ void __iomem *ss_reg_base; if (info->used_phy_port == 1) ss_reg_base = info->regs_base_2nd; else ss_reg_base = info->regs_base; ssp_tune0 = readl(ss_reg_base + EXYNOS_USBCON_SSP_PARACON0); ssp_tune1 = readl(ss_reg_base + EXYNOS_USBCON_SSP_PARACON1); cnt = 0; for (; info->tune_param[cnt].value != EXYNOS_USB_TUNE_LAST; cnt++) { char *para_name; int val; val = info->tune_param[cnt].value; if (val == -1) continue; para_name = info->tune_param[cnt].name; /* SSP PARACON0 setting : 0x135e_0000 + 0x34 */ if (!strcmp(para_name, "tx0_term_offset")) { ssp_tune0 &= ~SSP_PARACON0_TX0_TERM_OFFSET_MASK; ssp_tune0 |= SSP_PARACON0_TX0_TERM_OFFSET(val); } else if (!strcmp(para_name, "pcs_tx_swing_full")) { ssp_tune0 &= ~SSP_PARACON0_PCS_TX_SWING_FULL_MASK; ssp_tune0 |= SSP_PARACON0_PCS_TX_SWING_FULL(val); } else if (!strcmp(para_name, "pcs_tx_deemph_6db")) { ssp_tune0 &= ~SSP_PARACON0_PCS_TX_DEEMPH_6DB_MASK; ssp_tune0 |= SSP_PARACON0_PCS_TX_DEEMPH_6DB(val); } else if (!strcmp(para_name, "pcs_tx_deemph_3p5db")) { ssp_tune0 &= ~SSP_PARACON0_PCS_TX_DEEMPH_3P5DB_MASK; ssp_tune0 |= SSP_PARACON0_PCS_TX_DEEMPH_3P5DB(val); /* SSP PARACON1 setting : 0x135e_0000 + 0x38 */ } else if (!strcmp(para_name, "tx_vboost_lvl")) { ssp_tune1 &= ~SSP_PARACON1_TX_VBOOST_LVL_MASK; ssp_tune1 |= SSP_PARACON1_TX_VBOOST_LVL(val); } else if (!strcmp(para_name, "los_level")) { ssp_tune1 &= ~SSP_PARACON1_LOS_LEVEL_MASK; ssp_tune1 |= SSP_PARACON1_LOS_LEVEL(val); } else if (!strcmp(para_name, "los_bias")) { ssp_tune1 &= ~SSP_PARACON1_LOS_BIAS_MASK; ssp_tune1 |= SSP_PARACON1_LOS_BIAS(val); } else if (!strcmp(para_name, "pcs_rx_los_mask_val")) { ssp_tune1 &= ~SSP_PARACON1_PCS_RX_LOS_MASK_VAL_MASK; ssp_tune1 |= SSP_PARACON1_PCS_RX_LOS_MASK_VAL(val); } } /* for */ writel(ssp_tune0, ss_reg_base + EXYNOS_USBCON_SSP_PARACON0); writel(ssp_tune1, ss_reg_base + EXYNOS_USBCON_SSP_PARACON1); } /* else */ } void phy_exynos_usb_v3p1_tune_each(struct exynos_usbphy_info *info, char *para_name, int val) { u32 reg; void __iomem *regs_base = info->regs_base; if (val == -1) return; reg = readl(regs_base + EXYNOS_USBCON_HSP_TUNE); if (!strcmp(para_name, "compdis")) { reg &= ~HSP_TUNE_COMPDIS_MASK; reg |= HSP_TUNE_COMPDIS_SET(val); } else if (!strcmp(para_name, "otg")) { reg &= ~HSP_TUNE_OTG_MASK; reg |= HSP_TUNE_OTG_SET(val); } else if (!strcmp(para_name, "rx_sqrx")) { reg &= ~HSP_TUNE_SQRX_MASK; reg |= HSP_TUNE_SQRX_SET(val); } else if (!strcmp(para_name, "tx_fsls")) { reg &= ~HSP_TUNE_TXFSLS_MASK; reg |= HSP_TUNE_TXFSLS_SET(val); } else if (!strcmp(para_name, "tx_hsxv")) { reg &= ~HSP_TUNE_HSXV_MASK; reg |= HSP_TUNE_HSXV_SET(val); } else if (!strcmp(para_name, "tx_pre_emp")) { reg &= ~HSP_TUNE_TXPREEMPA_MASK; reg |= HSP_TUNE_TXPREEMPA_SET(val); } else if (!strcmp(para_name, "tx_pre_emp_plus")) { if (val) reg |= HSP_TUNE_TXPREEMPA_PLUS; else reg &= ~HSP_TUNE_TXPREEMPA_PLUS; } else if (!strcmp(para_name, "tx_res")) { reg &= ~HSP_TUNE_TXRES_MASK; reg |= HSP_TUNE_TXRES_SET(val); } else if (!strcmp(para_name, "tx_rise")) { reg &= ~HSP_TUNE_TXRISE_MASK; reg |= HSP_TUNE_TXRISE_SET(val); } else if (!strcmp(para_name, "tx_vref")) { reg &= ~HSP_TUNE_TXVREF_MASK; reg |= HSP_TUNE_TXVREF_SET(val); } writel(reg, regs_base + EXYNOS_USBCON_HSP_TUNE); } void phy_exynos_usb_v3p1_wr_tune_reg(struct exynos_usbphy_info *info, u32 val) { void __iomem *regs_base = info->regs_base; writel(val, regs_base + EXYNOS_USBCON_HSP_TUNE); } void phy_exynos_usb_v3p1_rd_tune_reg(struct exynos_usbphy_info *info, u32 *val) { void __iomem *regs_base = info->regs_base; if (!val) return; *val = readl(regs_base + EXYNOS_USBCON_HSP_TUNE); } void phy_exynos_usb3p1_rewa_ready(struct exynos_usbphy_info *info) { u32 reg; void __iomem *regs_base = info->regs_base; /* Disable ReWA */ reg = readl(regs_base + EXYNOS_USBCON_REWA_ENABLE); reg &= ~REWA_ENABLE_HS_REWA_EN; writel(reg, regs_base + EXYNOS_USBCON_REWA_ENABLE); /* Config ReWA Operation */ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_CTRL); /* * Select line state check circuit * 0 : FSVPLUS/FSMINUS * 1 : LINE STATE */ reg &= ~HSREWA_CTRL_DPDM_MON_SEL; /* * Select Drive K circuit * 0 : Auto Resume in the PHY * 1 : BYPASS mode by ReWA */ reg |= HSREWA_CTRL_DIG_BYPASS_CON_EN; writel(reg, regs_base + EXYNOS_USBCON_HSREWA_CTRL); /* Set Driver K Time */ reg = 0x1; writel(reg, regs_base + EXYNOS_USBCON_HSREWA_HSTK); /* Set Timeout counter Driver K Time */ reg = 0xff00; writel(reg, regs_base + EXYNOS_USBCON_HSREWA_REFTO); /* Disable All events source for abnormal event generation */ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK); reg |= HSREWA_CTRL_HS_EVT_ERR_SUS | HSREWA_CTRL_HS_EVT_ERR_DEV_K | HSREWA_CTRL_HS_EVT_DISCON | HSREWA_CTRL_HS_EVT_BYPASS_DIS | HSREWA_CTRL_HS_EVT_RET_DIS | HSREWA_CTRL_HS_EVT_RET_EN; writel(reg, regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK); } int phy_exynos_usb3p1_rewa_enable(struct exynos_usbphy_info *info) { int cnt; u32 reg; void __iomem *regs_base = info->regs_base; /* Clear the system valid flag */ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_CTRL); reg &= ~HSREWA_CTRL_HS_SYS_VALID; writel(reg, regs_base + EXYNOS_USBCON_HSREWA_CTRL); /* Enable ReWA */ reg = readl(regs_base + EXYNOS_USBCON_REWA_ENABLE); reg |= REWA_ENABLE_HS_REWA_EN; writel(reg, regs_base + EXYNOS_USBCON_REWA_ENABLE); /* Check Status : Wait ReWA Status is retention enabled */ for (cnt = 10000; cnt != 0; cnt--) { reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_EVT); /* non suspend status*/ if (reg & HSREWA_CTRL_HS_EVT_ERR_SUS) return HS_REWA_EN_STS_NOT_SUSPEND; /* Disconnect Status */ if (reg & HSREWA_CTRL_HS_EVT_DISCON) return HS_REWA_EN_STS_DISCONNECT; /* Success ReWA Enable */ if (reg & HSREWA_CTRL_HS_EVT_RET_EN) break; udelay(30); } if (!cnt) return HS_REWA_EN_STS_NOT_SUSPEND; /* Set the INT1 for detect K and Disconnect */ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK); reg &= ~HSREWA_CTRL_HS_EVT_DISCON & ~HSREWA_CTRL_HS_EVT_ERR_DEV_K; reg |= HSREWA_CTRL_HS_EVT_ERR_SUS | HSREWA_CTRL_HS_EVT_BYPASS_DIS | HSREWA_CTRL_HS_EVT_RET_DIS | HSREWA_CTRL_HS_EVT_RET_EN; writel(reg, regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK); /* Enable All interrupt source and disnable Wake-up event */ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INTR); reg &= ~HSREWA_INTR_WAKEUP_REQ_MASK & ~HSREWA_INTR_EVT_MASK & ~HSREWA_INTR_WAKEUP_MASK & ~HSREWA_INTR_TIMEOUT_MASK; writel(reg, regs_base + EXYNOS_USBCON_HSREWA_INTR); udelay(100); return HS_REWA_EN_STS_ENALBED; } int phy_exynos_usb3p1_rewa_req_sys_valid(struct exynos_usbphy_info *info) { int cnt; u32 reg; void __iomem *regs_base = info->regs_base; /* Mask All Interrupt source for the INT1 */ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK); reg &= ~HSREWA_CTRL_HS_EVT_DISCON & ~HSREWA_CTRL_HS_EVT_ERR_DEV_K & ~HSREWA_CTRL_HS_EVT_ERR_SUS & ~HSREWA_CTRL_HS_EVT_BYPASS_DIS & ~HSREWA_CTRL_HS_EVT_RET_DIS & ~HSREWA_CTRL_HS_EVT_RET_EN; writel(reg, regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK); /* Enable All interrupt source and disnable Wake-up event */ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INTR); reg |= HSREWA_INTR_WAKEUP_REQ_MASK; reg |= HSREWA_INTR_TIMEOUT_MASK; reg |= HSREWA_INTR_EVT_MASK; reg |= HSREWA_INTR_WAKEUP_MASK; writel(reg, regs_base + EXYNOS_USBCON_HSREWA_INTR); /* Set the system valid flag */ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_CTRL); reg |= HSREWA_CTRL_HS_SYS_VALID; writel(reg, regs_base + EXYNOS_USBCON_HSREWA_CTRL); for (cnt = 10000; cnt != 0; cnt--) { reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_EVT); /* Disconnect Status */ if (reg & HSREWA_CTRL_HS_EVT_DISCON) return HS_REWA_EN_STS_DISCONNECT; /* Success ReWA Enable */ if (reg & HSREWA_CTRL_HS_EVT_RET_EN) break; udelay(30); } return HS_REWA_EN_STS_DISABLED; } int phy_exynos_usb3p1_rewa_disable(struct exynos_usbphy_info *info) { int cnt; u32 reg; void __iomem *regs_base = info->regs_base; /* * Check ReWA Already diabled * If ReWA was disabled states, disabled sequence is already done */ reg = readl(regs_base + EXYNOS_USBCON_REWA_ENABLE); if (!(reg & REWA_ENABLE_HS_REWA_EN)) return 0; /* Set Link ready to notify ReWA */ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_CTRL); reg |= HSREWA_CTRL_HS_LINK_READY; writel(reg, regs_base + EXYNOS_USBCON_HSREWA_CTRL); /* Wait Bypass Disable */ for (cnt = 10000; cnt != 0; cnt--) { reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_EVT); /* Success ReWA Enable */ if (reg & HSREWA_CTRL_HS_EVT_BYPASS_DIS) break; udelay(30); } if (!cnt) return -1; /* Wait ReWA Done */ for (cnt = 1000; cnt != 0; cnt--) { reg = readl(regs_base + EXYNOS_USBCON_HSREWA_CTRL); /* Success ReWA Enable */ if (reg & HSREWA_CTRL_HS_REWA_DONE) break; udelay(30); } if (!cnt) return -1; /* Disable ReWA */ reg = readl(regs_base + EXYNOS_USBCON_REWA_ENABLE); reg &= ~REWA_ENABLE_HS_REWA_EN; writel(reg, regs_base + EXYNOS_USBCON_REWA_ENABLE); return 0; } int phy_exynos_usb3p1_rewa_cancel(struct exynos_usbphy_info *info) { int ret; u32 reg; void __iomem *regs_base = info->regs_base; ret = phy_exynos_usb3p1_rewa_req_sys_valid(info); /* Disable ReWA */ reg = readl(regs_base + EXYNOS_USBCON_REWA_ENABLE); reg &= ~REWA_ENABLE_HS_REWA_EN; writel(reg, regs_base + EXYNOS_USBCON_REWA_ENABLE); udelay(100); return ret; }