#define CLK_STOP_MASK          (REFCLKOUT_STOP | REFCLK_STOP |\
                                 UNIPRO_MCLK_STOP | MPHY_APBCLK_STOP|\
                                 UNIPRO_PCLK_STOP)
+/* HCI_MISC is also known as HCI_FORCE_HCS */
 #define HCI_MISC               0xB4
 #define REFCLK_CTRL_EN         BIT(7)
 #define UNIPRO_PCLK_CTRL_EN    BIT(6)
 /*
  * UNIPRO registers
  */
+#define UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER0 0x7888
+#define UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER1 0x788c
+#define UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER2 0x7890
 #define UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER0        0x78B8
 #define UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER1        0x78BC
 #define UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER2        0x78C0
        if (attr->pa_dbg_opt_suite1_off)
                ufshcd_dme_set(hba, UIC_ARG_MIB(attr->pa_dbg_opt_suite1_off),
                               attr->pa_dbg_opt_suite1_val);
+
+       if (attr->pa_dbg_opt_suite2_off)
+               ufshcd_dme_set(hba, UIC_ARG_MIB(attr->pa_dbg_opt_suite2_off),
+                              attr->pa_dbg_opt_suite2_val);
 }
 
 static void exynos_ufs_config_intr(struct exynos_ufs *ufs, u32 errs, u8 index)
        return 0;
 }
 
+static inline u32 get_mclk_period_unipro_18(struct exynos_ufs *ufs)
+{
+       return (16 * 1000 * 1000000UL / ufs->mclk_rate);
+}
+
+static int gs101_ufs_pre_link(struct exynos_ufs *ufs)
+{
+       struct ufs_hba *hba = ufs->hba;
+       int i;
+       u32 tx_line_reset_period, rx_line_reset_period;
+
+       rx_line_reset_period = (RX_LINE_RESET_TIME * ufs->mclk_rate)
+                               / NSEC_PER_MSEC;
+       tx_line_reset_period = (TX_LINE_RESET_TIME * ufs->mclk_rate)
+                               / NSEC_PER_MSEC;
+
+       unipro_writel(ufs, get_mclk_period_unipro_18(ufs), COMP_CLK_PERIOD);
+
+       ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x40);
+
+       for_each_ufs_rx_lane(ufs, i) {
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD, i),
+                              DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate));
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD_EN, i), 0x0);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE2, i),
+                              (rx_line_reset_period >> 16) & 0xFF);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE1, i),
+                              (rx_line_reset_period >> 8) & 0xFF);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE0, i),
+                              (rx_line_reset_period) & 0xFF);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x2f, i), 0x69);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x84, i), 0x1);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x25, i), 0xf6);
+       }
+
+       for_each_ufs_tx_lane(ufs, i) {
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD, i),
+                              DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate));
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD_EN, i),
+                              0x02);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE2, i),
+                              (tx_line_reset_period >> 16) & 0xFF);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE1, i),
+                              (tx_line_reset_period >> 8) & 0xFF);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE0, i),
+                              (tx_line_reset_period) & 0xFF);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x04, i), 1);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x7F, i), 0);
+       }
+
+       ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x0);
+       ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0x0);
+       ufshcd_dme_set(hba, UIC_ARG_MIB(N_DEVICEID), 0x0);
+       ufshcd_dme_set(hba, UIC_ARG_MIB(N_DEVICEID_VALID), 0x1);
+       ufshcd_dme_set(hba, UIC_ARG_MIB(T_PEERDEVICEID), 0x1);
+       ufshcd_dme_set(hba, UIC_ARG_MIB(T_CONNECTIONSTATE), CPORT_CONNECTED);
+       ufshcd_dme_set(hba, UIC_ARG_MIB(0xA006), 0x8000);
+
+       return 0;
+}
+
+static int gs101_ufs_post_link(struct exynos_ufs *ufs)
+{
+       struct ufs_hba *hba = ufs->hba;
+
+       exynos_ufs_enable_dbg_mode(hba);
+       ufshcd_dme_set(hba, UIC_ARG_MIB(PA_SAVECONFIGTIME), 0x3e8);
+       exynos_ufs_disable_dbg_mode(hba);
+
+       return 0;
+}
+
+static int gs101_ufs_pre_pwr_change(struct exynos_ufs *ufs,
+                                        struct ufs_pa_layer_attr *pwr)
+{
+       struct ufs_hba *hba = ufs->hba;
+
+       ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA0), 12000);
+       ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA1), 32000);
+       ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA2), 16000);
+       unipro_writel(ufs, 8064, UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER0);
+       unipro_writel(ufs, 28224, UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER1);
+       unipro_writel(ufs, 20160, UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER2);
+       unipro_writel(ufs, 12000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER0);
+       unipro_writel(ufs, 32000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER1);
+       unipro_writel(ufs, 16000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER2);
+
+       return 0;
+}
+
 static const struct ufs_hba_variant_ops ufs_hba_exynos_ops = {
        .name                           = "exynos_ufs",
        .init                           = exynos_ufs_init,
        .post_pwr_change        = exynos7_ufs_post_pwr_change,
 };
 
+static struct exynos_ufs_uic_attr gs101_uic_attr = {
+       .tx_trailingclks                = 0xff,
+       .tx_dif_p_nsec                  = 3000000,      /* unit: ns */
+       .tx_dif_n_nsec                  = 1000000,      /* unit: ns */
+       .tx_high_z_cnt_nsec             = 20000,        /* unit: ns */
+       .tx_base_unit_nsec              = 100000,       /* unit: ns */
+       .tx_gran_unit_nsec              = 4000,         /* unit: ns */
+       .tx_sleep_cnt                   = 1000,         /* unit: ns */
+       .tx_min_activatetime            = 0xa,
+       .rx_filler_enable               = 0x2,
+       .rx_dif_p_nsec                  = 1000000,      /* unit: ns */
+       .rx_hibern8_wait_nsec           = 4000000,      /* unit: ns */
+       .rx_base_unit_nsec              = 100000,       /* unit: ns */
+       .rx_gran_unit_nsec              = 4000,         /* unit: ns */
+       .rx_sleep_cnt                   = 1280,         /* unit: ns */
+       .rx_stall_cnt                   = 320,          /* unit: ns */
+       .rx_hs_g1_sync_len_cap          = SYNC_LEN_COARSE(0xf),
+       .rx_hs_g2_sync_len_cap          = SYNC_LEN_COARSE(0xf),
+       .rx_hs_g3_sync_len_cap          = SYNC_LEN_COARSE(0xf),
+       .rx_hs_g1_prep_sync_len_cap     = PREP_LEN(0xf),
+       .rx_hs_g2_prep_sync_len_cap     = PREP_LEN(0xf),
+       .rx_hs_g3_prep_sync_len_cap     = PREP_LEN(0xf),
+       .pa_dbg_opt_suite1_val          = 0x90913C1C,
+       .pa_dbg_opt_suite1_off          = PA_GS101_DBG_OPTION_SUITE1,
+       .pa_dbg_opt_suite2_val          = 0xE01C115F,
+       .pa_dbg_opt_suite2_off          = PA_GS101_DBG_OPTION_SUITE2,
+};
+
 static struct exynos_ufs_uic_attr fsd_uic_attr = {
        .tx_trailingclks                = 0x10,
        .tx_dif_p_nsec                  = 3000000,      /* unit: ns */
        .pre_pwr_change         = fsd_ufs_pre_pwr_change,
 };
 
+static const struct exynos_ufs_drv_data gs101_ufs_drvs = {
+       .uic_attr               = &gs101_uic_attr,
+       .quirks                 = UFSHCD_QUIRK_PRDT_BYTE_GRAN |
+                                 UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR |
+                                 UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR |
+                                 UFSHCD_QUIRK_BROKEN_OCS_FATAL_ERROR |
+                                 UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL |
+                                 UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING,
+       .opts                   = EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL |
+                                 EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR |
+                                 EXYNOS_UFS_OPT_UFSPR_SECURE |
+                                 EXYNOS_UFS_OPT_TIMER_TICK_SELECT,
+       .drv_init               = exynosauto_ufs_drv_init,
+       .pre_link               = gs101_ufs_pre_link,
+       .post_link              = gs101_ufs_post_link,
+       .pre_pwr_change         = gs101_ufs_pre_pwr_change,
+};
+
 static const struct of_device_id exynos_ufs_of_match[] = {
+       { .compatible = "google,gs101-ufs",
+         .data       = &gs101_ufs_drvs },
        { .compatible = "samsung,exynos7-ufs",
          .data       = &exynos_ufs_drvs },
        { .compatible = "samsung,exynosautov9-ufs",