xhci: Add SuperSpeedPlus high bandwidth isoc support to xhci endpoints
[deliverable/linux.git] / drivers / phy / phy-mt65xx-usb3.c
index e427c3b788ff35e23a49808116936f0d5f39ffa4..c0e7b4b0cf5c66c942977c36b9d5f46ef97ca4e8 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/of_address.h>
 #include <linux/phy/phy.h>
@@ -27,6 +28,7 @@
  * relative to USB3_SIF2_BASE base address
  */
 #define SSUSB_SIFSLV_SPLLC             0x0000
+#define SSUSB_SIFSLV_U2FREQ            0x0100
 
 /* offsets of sub-segment in each port registers */
 #define SSUSB_SIFSLV_U2PHY_COM_BASE    0x0000
@@ -41,6 +43,7 @@
 #define PA2_RG_SIF_U2PLL_FORCE_EN      BIT(18)
 
 #define U3P_USBPHYACR5         (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0014)
+#define PA5_RG_U2_HSTX_SRCAL_EN        BIT(15)
 #define PA5_RG_U2_HSTX_SRCTRL          GENMASK(14, 12)
 #define PA5_RG_U2_HSTX_SRCTRL_VAL(x)   ((0x7 & (x)) << 12)
 #define PA5_RG_U2_HS_100U_U3_EN        BIT(11)
@@ -49,6 +52,8 @@
 #define PA6_RG_U2_ISO_EN               BIT(31)
 #define PA6_RG_U2_BC11_SW_EN           BIT(23)
 #define PA6_RG_U2_OTG_VBUSCMP_EN       BIT(20)
+#define PA6_RG_U2_SQTH         GENMASK(3, 0)
+#define PA6_RG_U2_SQTH_VAL(x)  (0xf & (x))
 
 #define U3P_U2PHYACR4          (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0020)
 #define P2C_RG_USB20_GPIO_CTL          BIT(9)
 #define XC3_RG_U3_XTAL_RX_PWD          BIT(9)
 #define XC3_RG_U3_FRC_XTAL_RX_PWD      BIT(8)
 
+#define U3P_U2FREQ_FMCR0       (SSUSB_SIFSLV_U2FREQ + 0x00)
+#define P2F_RG_MONCLK_SEL      GENMASK(27, 26)
+#define P2F_RG_MONCLK_SEL_VAL(x)       ((0x3 & (x)) << 26)
+#define P2F_RG_FREQDET_EN      BIT(24)
+#define P2F_RG_CYCLECNT                GENMASK(23, 0)
+#define P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x))
+
+#define U3P_U2FREQ_VALUE       (SSUSB_SIFSLV_U2FREQ + 0x0c)
+
+#define U3P_U2FREQ_FMMONR1     (SSUSB_SIFSLV_U2FREQ + 0x10)
+#define P2F_USB_FM_VALID       BIT(0)
+#define P2F_RG_FRCK_EN         BIT(8)
+
+#define U3P_REF_CLK            26      /* MHZ */
+#define U3P_SLEW_RATE_COEF     28
+#define U3P_SR_COEF_DIVISOR    1000
+#define U3P_FM_DET_CYCLE_CNT   1024
+
 struct mt65xx_phy_instance {
        struct phy *phy;
        void __iomem *port_base;
@@ -126,6 +149,77 @@ struct mt65xx_u3phy {
        int nphys;
 };
 
+static void hs_slew_rate_calibrate(struct mt65xx_u3phy *u3phy,
+       struct mt65xx_phy_instance *instance)
+{
+       void __iomem *sif_base = u3phy->sif_base;
+       int calibration_val;
+       int fm_out;
+       u32 tmp;
+
+       /* enable USB ring oscillator */
+       tmp = readl(instance->port_base + U3P_USBPHYACR5);
+       tmp |= PA5_RG_U2_HSTX_SRCAL_EN;
+       writel(tmp, instance->port_base + U3P_USBPHYACR5);
+       udelay(1);
+
+       /*enable free run clock */
+       tmp = readl(sif_base + U3P_U2FREQ_FMMONR1);
+       tmp |= P2F_RG_FRCK_EN;
+       writel(tmp, sif_base + U3P_U2FREQ_FMMONR1);
+
+       /* set cycle count as 1024, and select u2 channel */
+       tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
+       tmp &= ~(P2F_RG_CYCLECNT | P2F_RG_MONCLK_SEL);
+       tmp |= P2F_RG_CYCLECNT_VAL(U3P_FM_DET_CYCLE_CNT);
+       tmp |= P2F_RG_MONCLK_SEL_VAL(instance->index);
+       writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
+
+       /* enable frequency meter */
+       tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
+       tmp |= P2F_RG_FREQDET_EN;
+       writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
+
+       /* ignore return value */
+       readl_poll_timeout(sif_base + U3P_U2FREQ_FMMONR1, tmp,
+                 (tmp & P2F_USB_FM_VALID), 10, 200);
+
+       fm_out = readl(sif_base + U3P_U2FREQ_VALUE);
+
+       /* disable frequency meter */
+       tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
+       tmp &= ~P2F_RG_FREQDET_EN;
+       writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
+
+       /*disable free run clock */
+       tmp = readl(sif_base + U3P_U2FREQ_FMMONR1);
+       tmp &= ~P2F_RG_FRCK_EN;
+       writel(tmp, sif_base + U3P_U2FREQ_FMMONR1);
+
+       if (fm_out) {
+               /* ( 1024 / FM_OUT ) x reference clock frequency x 0.028 */
+               tmp = U3P_FM_DET_CYCLE_CNT * U3P_REF_CLK * U3P_SLEW_RATE_COEF;
+               tmp /= fm_out;
+               calibration_val = DIV_ROUND_CLOSEST(tmp, U3P_SR_COEF_DIVISOR);
+       } else {
+               /* if FM detection fail, set default value */
+               calibration_val = 4;
+       }
+       dev_dbg(u3phy->dev, "phy:%d, fm_out:%d, calib:%d\n",
+               instance->index, fm_out, calibration_val);
+
+       /* set HS slew rate */
+       tmp = readl(instance->port_base + U3P_USBPHYACR5);
+       tmp &= ~PA5_RG_U2_HSTX_SRCTRL;
+       tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(calibration_val);
+       writel(tmp, instance->port_base + U3P_USBPHYACR5);
+
+       /* disable USB ring oscillator */
+       tmp = readl(instance->port_base + U3P_USBPHYACR5);
+       tmp &= ~PA5_RG_U2_HSTX_SRCAL_EN;
+       writel(tmp, instance->port_base + U3P_USBPHYACR5);
+}
+
 static void phy_instance_init(struct mt65xx_u3phy *u3phy,
        struct mt65xx_phy_instance *instance)
 {
@@ -165,9 +259,10 @@ static void phy_instance_init(struct mt65xx_u3phy *u3phy,
                writel(tmp, port_base + U3P_U2PHYDTM0);
        }
 
-       /* DP/DM BC1.1 path Disable */
        tmp = readl(port_base + U3P_USBPHYACR6);
-       tmp &= ~PA6_RG_U2_BC11_SW_EN;
+       tmp &= ~PA6_RG_U2_BC11_SW_EN;   /* DP/DM BC1.1 path Disable */
+       tmp &= ~PA6_RG_U2_SQTH;
+       tmp |= PA6_RG_U2_SQTH_VAL(2);
        writel(tmp, port_base + U3P_USBPHYACR6);
 
        tmp = readl(port_base + U3P_U3PHYA_DA_REG0);
@@ -223,9 +318,9 @@ static void phy_instance_power_on(struct mt65xx_u3phy *u3phy,
                tmp |= XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD;
                writel(tmp, u3phy->sif_base + U3P_XTALCTL3);
 
-               /* [mt8173]disable Change 100uA current from SSUSB */
+               /* [mt8173]switch 100uA current to SSUSB */
                tmp = readl(port_base + U3P_USBPHYACR5);
-               tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
+               tmp |= PA5_RG_U2_HS_100U_U3_EN;
                writel(tmp, port_base + U3P_USBPHYACR5);
        }
 
@@ -270,7 +365,7 @@ static void phy_instance_power_off(struct mt65xx_u3phy *u3phy,
        writel(tmp, port_base + U3P_USBPHYACR6);
 
        if (!index) {
-               /* (also disable)Change 100uA current switch to USB2.0 */
+               /* switch 100uA current back to USB2.0 */
                tmp = readl(port_base + U3P_USBPHYACR5);
                tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
                writel(tmp, port_base + U3P_USBPHYACR5);
@@ -340,6 +435,7 @@ static int mt65xx_phy_power_on(struct phy *phy)
        struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
 
        phy_instance_power_on(u3phy, instance);
+       hs_slew_rate_calibrate(u3phy, instance);
        return 0;
 }
 
This page took 0.033287 seconds and 5 git commands to generate.