diff options
Diffstat (limited to 'drivers/phy')
| -rw-r--r-- | drivers/phy/Makefile | 3 | ||||
| -rw-r--r-- | drivers/phy/phy-qcom-ufs-i.h | 34 | ||||
| -rw-r--r-- | drivers/phy/phy-qcom-ufs-qmp-14nm.c | 146 | ||||
| -rw-r--r-- | drivers/phy/phy-qcom-ufs-qmp-14nm.h | 244 | ||||
| -rw-r--r-- | drivers/phy/phy-qcom-ufs-qmp-20nm.c | 22 | ||||
| -rw-r--r-- | drivers/phy/phy-qcom-ufs-qmp-20nm.h | 2 | ||||
| -rw-r--r-- | drivers/phy/phy-qcom-ufs-qmp-v3-660.c | 260 | ||||
| -rw-r--r-- | drivers/phy/phy-qcom-ufs-qmp-v3-660.h | 283 | ||||
| -rw-r--r-- | drivers/phy/phy-qcom-ufs-qmp-v3.c | 298 | ||||
| -rw-r--r-- | drivers/phy/phy-qcom-ufs-qmp-v3.h | 387 | ||||
| -rw-r--r-- | drivers/phy/phy-qcom-ufs-qrbtc-v2.c | 205 | ||||
| -rw-r--r-- | drivers/phy/phy-qcom-ufs-qrbtc-v2.h | 116 | ||||
| -rw-r--r-- | drivers/phy/phy-qcom-ufs.c | 235 |
13 files changed, 2120 insertions, 115 deletions
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 075db1a81aa5..e1ef353aa1e1 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -44,6 +44,9 @@ obj-$(CONFIG_PHY_STIH41X_USB) += phy-stih41x-usb.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o +obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-v3.o +obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qrbtc-v2.o +obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-v3-660.o obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o diff --git a/drivers/phy/phy-qcom-ufs-i.h b/drivers/phy/phy-qcom-ufs-i.h index 2bd5ce43a724..7acef104d5b7 100644 --- a/drivers/phy/phy-qcom-ufs-i.h +++ b/drivers/phy/phy-qcom-ufs-i.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. + * Copyright (c) 2013-2016, Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -91,6 +91,7 @@ struct ufs_qcom_phy { struct clk *ref_clk_src; struct clk *ref_clk_parent; struct clk *ref_clk; + struct clk *ref_aux_clk; bool is_ref_clk_enabled; bool is_dev_ref_clk_enabled; struct ufs_qcom_phy_vreg vdda_pll; @@ -107,6 +108,23 @@ struct ufs_qcom_phy { */ #define UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE BIT(0) + /* + * On some UFS PHY HW revisions, UFS PHY power up calibration sequence + * cannot have SVS mode configuration otherwise calibration result + * cannot be used in HS-G3. So there are additional register writes must + * be done after the PHY is initialized but before the controller + * requests hibernate exit. + */ + #define UFS_QCOM_PHY_QUIRK_SVS_MODE BIT(1) + + /* + * On some UFS PHY HW revisions, UFS PHY power up calibration sequence + * requires manual VCO tuning code and its better to rely on the VCO + * tuning code programmed by boot loader. Enable this quirk to enable + * programming the manually tuned VCO code. + */ + #define UFS_QCOM_PHY_QUIRK_VCO_MANUAL_TUNING BIT(2) + u8 host_ctrl_rev_major; u16 host_ctrl_rev_minor; u16 host_ctrl_rev_step; @@ -116,6 +134,7 @@ struct ufs_qcom_phy { int cached_regs_table_size; bool is_powered_on; struct ufs_qcom_phy_specific_ops *phy_spec_ops; + u32 vco_tune1_mode1; }; /** @@ -127,15 +146,23 @@ struct ufs_qcom_phy { * @is_physical_coding_sublayer_ready: pointer to a function that * checks pcs readiness. returns 0 for success and non-zero for error. * @set_tx_lane_enable: pointer to a function that enable tx lanes + * @ctrl_rx_linecfg: pointer to a function that controls the Host Rx LineCfg + * state. * @power_control: pointer to a function that controls analog rail of phy * and writes to QSERDES_RX_SIGDET_CNTRL attribute + * @configure_lpm: pointer to a function that configures the phy + * for low power mode. + * @dbg_register_dump: pointer to a function that dumps phy registers for debug. */ struct ufs_qcom_phy_specific_ops { int (*calibrate_phy)(struct ufs_qcom_phy *phy, bool is_rate_B); void (*start_serdes)(struct ufs_qcom_phy *phy); int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy); void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val); + void (*ctrl_rx_linecfg)(struct ufs_qcom_phy *phy, bool ctrl); void (*power_control)(struct ufs_qcom_phy *phy, bool val); + int (*configure_lpm)(struct ufs_qcom_phy *phy, bool enable); + void (*dbg_register_dump)(struct ufs_qcom_phy *phy); }; struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy); @@ -156,4 +183,9 @@ int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, struct ufs_qcom_phy_calibration *tbl_A, int tbl_size_A, struct ufs_qcom_phy_calibration *tbl_B, int tbl_size_B, bool is_rate_B); +void ufs_qcom_phy_write_tbl(struct ufs_qcom_phy *ufs_qcom_phy, + struct ufs_qcom_phy_calibration *tbl, + int tbl_size); +void ufs_qcom_phy_dump_regs(struct ufs_qcom_phy *phy, + int offset, int len, char *prefix); #endif diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/phy-qcom-ufs-qmp-14nm.c index 56631e77c11d..8cb8c02f4cb8 100644 --- a/drivers/phy/phy-qcom-ufs-qmp-14nm.c +++ b/drivers/phy/phy-qcom-ufs-qmp-14nm.c @@ -15,19 +15,49 @@ #include "phy-qcom-ufs-qmp-14nm.h" #define UFS_PHY_NAME "ufs_phy_qmp_14nm" -#define UFS_PHY_VDDA_PHY_UV (925000) static int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, bool is_rate_B) { - int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A); - int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); int err; + int tbl_size_A, tbl_size_B; + struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; + u8 major = ufs_qcom_phy->host_ctrl_rev_major; + u16 minor = ufs_qcom_phy->host_ctrl_rev_minor; + u16 step = ufs_qcom_phy->host_ctrl_rev_step; + + tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); + tbl_B = phy_cal_table_rate_B; + + if ((major == 0x2) && (minor == 0x000) && (step == 0x0000)) { + tbl_A = phy_cal_table_rate_A_2_0_0; + tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_2_0_0); + } else if ((major == 0x2) && (minor == 0x001) && (step == 0x0000)) { + tbl_A = phy_cal_table_rate_A_2_1_0; + tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_2_1_0); + } else if ((major == 0x2) && (minor == 0x002) && (step == 0x0000)) { + tbl_A = phy_cal_table_rate_A_2_2_0; + tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_2_2_0); + tbl_B = phy_cal_table_rate_B_2_2_0; + tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B_2_2_0); + } else { + dev_err(ufs_qcom_phy->dev, + "%s: Unknown UFS-PHY version (major 0x%x minor 0x%x step 0x%x), no calibration values\n", + __func__, major, minor, step); + err = -ENODEV; + goto out; + } - err = ufs_qcom_phy_calibrate(ufs_qcom_phy, phy_cal_table_rate_A, - tbl_size_A, phy_cal_table_rate_B, tbl_size_B, is_rate_B); + err = ufs_qcom_phy_calibrate(ufs_qcom_phy, + tbl_A, tbl_size_A, + tbl_B, tbl_size_B, + is_rate_B); + if (ufs_qcom_phy->quirks & UFS_QCOM_PHY_QUIRK_VCO_MANUAL_TUNING) + writel_relaxed(ufs_qcom_phy->vco_tune1_mode1, + ufs_qcom_phy->mmio + QSERDES_COM_VCO_TUNE1_MODE1); +out: if (err) dev_err(ufs_qcom_phy->dev, "%s: ufs_qcom_phy_calibrate() failed %d\n", @@ -38,8 +68,15 @@ int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, static void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common) { - phy_common->quirks = - UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; + u8 major = phy_common->host_ctrl_rev_major; + u16 minor = phy_common->host_ctrl_rev_minor; + u16 step = phy_common->host_ctrl_rev_step; + + if ((major == 0x2) && (minor == 0x000) && (step == 0x0000)) + phy_common->quirks = + UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE | + UFS_QCOM_PHY_QUIRK_SVS_MODE | + UFS_QCOM_PHY_QUIRK_VCO_MANUAL_TUNING; } static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy) @@ -61,24 +98,66 @@ static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy) __func__, err); goto out; } - phy_common->vdda_phy.max_uV = UFS_PHY_VDDA_PHY_UV; - phy_common->vdda_phy.min_uV = UFS_PHY_VDDA_PHY_UV; ufs_qcom_phy_qmp_14nm_advertise_quirks(phy_common); + if (phy_common->quirks & UFS_QCOM_PHY_QUIRK_VCO_MANUAL_TUNING) { + phy_common->vco_tune1_mode1 = readl_relaxed(phy_common->mmio + + QSERDES_COM_VCO_TUNE1_MODE1); + dev_info(phy_common->dev, "%s: vco_tune1_mode1 0x%x\n", + __func__, phy_common->vco_tune1_mode1); + } + out: return err; } static -void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val) +void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, + bool power_ctrl) { - writel_relaxed(val ? 0x1 : 0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); - /* - * Before any transactions involving PHY, ensure PHY knows - * that it's analog rail is powered ON (or OFF). - */ - mb(); + bool is_workaround_req = false; + + if (phy->quirks & + UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE) + is_workaround_req = true; + + if (!power_ctrl) { + /* apply PHY analog power collapse */ + if (is_workaround_req) { + /* assert common reset before analog power collapse */ + writel_relaxed(0x1, phy->mmio + QSERDES_COM_SW_RESET); + /* + * make sure that reset is propogated before analog + * power collapse + */ + mb(); + } + /* apply analog power collapse */ + writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); + /* + * Make sure that PHY knows its analog rail is going to be + * powered OFF. + */ + mb(); + } else { + /* bring PHY out of analog power collapse */ + writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); + /* + * Before any transactions involving PHY, ensure PHY knows + * that it's analog rail is powered ON. + */ + mb(); + if (is_workaround_req) { + /* + * de-assert common reset after coming out of analog + * power collapse + */ + writel_relaxed(0x0, phy->mmio + QSERDES_COM_SW_RESET); + /* make common reset is de-asserted before proceeding */ + mb(); + } + } } static inline @@ -90,6 +169,23 @@ void ufs_qcom_phy_qmp_14nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) */ } +static +void ufs_qcom_phy_qmp_14nm_ctrl_rx_linecfg(struct ufs_qcom_phy *phy, bool ctrl) +{ + u32 temp; + + temp = readl_relaxed(phy->mmio + UFS_PHY_LINECFG_DISABLE); + + if (ctrl) /* enable RX LineCfg */ + temp &= ~UFS_PHY_RX_LINECFG_DISABLE_BIT; + else /* disable RX LineCfg */ + temp |= UFS_PHY_RX_LINECFG_DISABLE_BIT; + + writel_relaxed(temp, phy->mmio + UFS_PHY_LINECFG_DISABLE); + /* make sure that RX LineCfg config applied before we return */ + mb(); +} + static inline void ufs_qcom_phy_qmp_14nm_start_serdes(struct ufs_qcom_phy *phy) { u32 tmp; @@ -109,9 +205,24 @@ static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, val, (val & MASK_PCS_READY), 10, 1000000); - if (err) + if (err) { dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", __func__, err); + goto out; + } + + if (phy_common->quirks & UFS_QCOM_PHY_QUIRK_SVS_MODE) { + int i; + + for (i = 0; i < ARRAY_SIZE(phy_svs_mode_config_2_0_0); i++) + writel_relaxed(phy_svs_mode_config_2_0_0[i].cfg_value, + (phy_common->mmio + + phy_svs_mode_config_2_0_0[i].reg_offset)); + /* apply above configuration immediately */ + mb(); + } + +out: return err; } @@ -128,6 +239,7 @@ static struct ufs_qcom_phy_specific_ops phy_14nm_ops = { .start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes, .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready, .set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable, + .ctrl_rx_linecfg = ufs_qcom_phy_qmp_14nm_ctrl_rx_linecfg, .power_control = ufs_qcom_phy_qmp_14nm_power_control, }; diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.h b/drivers/phy/phy-qcom-ufs-qmp-14nm.h index 3aefdbacbcd0..46e652f34401 100644 --- a/drivers/phy/phy-qcom-ufs-qmp-14nm.h +++ b/drivers/phy/phy-qcom-ufs-qmp-14nm.h @@ -27,12 +27,14 @@ #define QSERDES_COM_BG_TIMER COM_OFF(0x0C) #define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x34) #define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x3C) +#define QSERDES_COM_PLL_IVCO COM_OFF(0x48) #define QSERDES_COM_LOCK_CMP1_MODE0 COM_OFF(0x4C) #define QSERDES_COM_LOCK_CMP2_MODE0 COM_OFF(0x50) #define QSERDES_COM_LOCK_CMP3_MODE0 COM_OFF(0x54) #define QSERDES_COM_LOCK_CMP1_MODE1 COM_OFF(0x58) #define QSERDES_COM_LOCK_CMP2_MODE1 COM_OFF(0x5C) #define QSERDES_COM_LOCK_CMP3_MODE1 COM_OFF(0x60) +#define QSERDES_COM_BG_TRIM COM_OFF(0x70) #define QSERDES_COM_CP_CTRL_MODE0 COM_OFF(0x78) #define QSERDES_COM_CP_CTRL_MODE1 COM_OFF(0x7C) #define QSERDES_COM_PLL_RCTRL_MODE0 COM_OFF(0x84) @@ -41,6 +43,7 @@ #define QSERDES_COM_PLL_CCTRL_MODE1 COM_OFF(0x94) #define QSERDES_COM_SYSCLK_EN_SEL COM_OFF(0xAC) #define QSERDES_COM_RESETSM_CNTRL COM_OFF(0xB4) +#define QSERDES_COM_RESCODE_DIV_NUM COM_OFF(0xC4) #define QSERDES_COM_LOCK_CMP_EN COM_OFF(0xC8) #define QSERDES_COM_LOCK_CMP_CFG COM_OFF(0xCC) #define QSERDES_COM_DEC_START_MODE0 COM_OFF(0xD0) @@ -61,19 +64,35 @@ #define QSERDES_COM_VCO_TUNE2_MODE0 COM_OFF(0x130) #define QSERDES_COM_VCO_TUNE1_MODE1 COM_OFF(0x134) #define QSERDES_COM_VCO_TUNE2_MODE1 COM_OFF(0x138) +#define QSERDES_COM_VCO_TUNE_INITVAL1 COM_OFF(0x13C) +#define QSERDES_COM_VCO_TUNE_INITVAL2 COM_OFF(0x140) #define QSERDES_COM_VCO_TUNE_TIMER1 COM_OFF(0x144) #define QSERDES_COM_VCO_TUNE_TIMER2 COM_OFF(0x148) #define QSERDES_COM_CLK_SELECT COM_OFF(0x174) #define QSERDES_COM_HSCLK_SEL COM_OFF(0x178) #define QSERDES_COM_CORECLK_DIV COM_OFF(0x184) +#define QSERDES_COM_SW_RESET COM_OFF(0x188) #define QSERDES_COM_CORE_CLK_EN COM_OFF(0x18C) #define QSERDES_COM_CMN_CONFIG COM_OFF(0x194) #define QSERDES_COM_SVS_MODE_CLK_SEL COM_OFF(0x19C) +#define QSERDES_COM_DEBUG_BUS0 COM_OFF(0x1A0) +#define QSERDES_COM_DEBUG_BUS1 COM_OFF(0x1A4) +#define QSERDES_COM_DEBUG_BUS2 COM_OFF(0x1A8) +#define QSERDES_COM_DEBUG_BUS3 COM_OFF(0x1AC) +#define QSERDES_COM_DEBUG_BUS_SEL COM_OFF(0x1B0) +#define QSERDES_COM_CMN_MISC2 COM_OFF(0x1B8) #define QSERDES_COM_CORECLK_DIV_MODE1 COM_OFF(0x1BC) /* UFS PHY registers */ #define UFS_PHY_PHY_START PHY_OFF(0x00) #define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04) +#define UFS_PHY_TX_LARGE_AMP_DRV_LVL PHY_OFF(0x34) +#define UFS_PHY_TX_SMALL_AMP_DRV_LVL PHY_OFF(0x3C) +#define UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAP PHY_OFF(0xCC) +#define UFS_PHY_LINECFG_DISABLE PHY_OFF(0x138) +#define UFS_PHY_RX_SYM_RESYNC_CTRL PHY_OFF(0x13C) +#define UFS_PHY_RX_SIGDET_CTRL2 PHY_OFF(0x148) +#define UFS_PHY_RX_PWM_GEAR_BAND PHY_OFF(0x154) #define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x168) /* UFS PHY TX registers */ @@ -81,7 +100,12 @@ #define QSERDES_TX_LANE_MODE TX_OFF(0, 0x94) /* UFS PHY RX registers */ +#define QSERDES_RX_UCDR_SVS_SO_GAIN_HALF RX_OFF(0, 0x30) +#define QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER RX_OFF(0, 0x34) +#define QSERDES_RX_UCDR_SVS_SO_GAIN_EIGHTH RX_OFF(0, 0x38) +#define QSERDES_RX_UCDR_SVS_SO_GAIN RX_OFF(0, 0x3C) #define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN RX_OFF(0, 0x40) +#define QSERDES_RX_UCDR_SO_SATURATION_ENABLE RX_OFF(0, 0x48) #define QSERDES_RX_RX_TERM_BW RX_OFF(0, 0x90) #define QSERDES_RX_RX_EQ_GAIN1_LSB RX_OFF(0, 0xC4) #define QSERDES_RX_RX_EQ_GAIN1_MSB RX_OFF(0, 0xC8) @@ -93,6 +117,8 @@ #define QSERDES_RX_SIGDET_DEGLITCH_CNTRL RX_OFF(0, 0x11C) #define QSERDES_RX_RX_INTERFACE_MODE RX_OFF(0, 0x12C) +#define UFS_PHY_RX_LINECFG_DISABLE_BIT BIT(1) + /* * This structure represents the 14nm specific phy. * common_cfg MUST remain the first field in this structure @@ -105,12 +131,102 @@ struct ufs_qcom_phy_qmp_14nm { struct ufs_qcom_phy common_cfg; }; -static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = { +static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_2_0_0[] = { + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x0e), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0x17), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x05), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x1C), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_CFG, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x14), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x0b), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0x3F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x0b), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE1, 0x00), + + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN, 0x45), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE, 0x06), + + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_LVL, 0x24), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x1E), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_BW, 0x5B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x3F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0D), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IVCO, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TRIM, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESCODE_DIV_NUM, 0x15), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_MISC2, 0x1F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SVS_SO_GAIN_HALF, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SVS_SO_GAIN, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SO_SATURATION_ENABLE, 0x4B), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6c), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x12), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAP, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SYM_RESYNC_CTRL, 0x03), + + /* + * UFS_PHY_RX_PWM_GEAR_BAND configuration is changed after the power up + * sequence so make sure that this register gets set to power on reset + * value. This is required in case power up sequence is initiated after + * this register value got changed to value other than power on reset + * value. + */ + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_PWM_GEAR_BAND, 0x55), +}; + +/* + * For 2.1.0 revision, SVS mode configuration can be part of PHY power + * up sequence itself. + */ +static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_2_1_0[] = { UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x0e), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xd7), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0x14), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x02), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0a), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x05), @@ -155,23 +271,133 @@ static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = { UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE1, 0x00), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN, 0x45), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE, 0x06), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_LVL, 0x24), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x18), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x40), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x1E), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_BW, 0x5B), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x0F), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x3F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0D), + + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IVCO, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TRIM, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_PWM_GEAR_BAND, 0x15), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESCODE_DIV_NUM, 0x15), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_MISC2, 0x1F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SVS_SO_GAIN_HALF, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SVS_SO_GAIN, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SO_SATURATION_ENABLE, 0x4B), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6c), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x12), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAP, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SYM_RESYNC_CTRL, 0x03), +}; + +static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_2_2_0[] = { + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x0e), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0x14), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_CFG, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x0b), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x0b), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE1, 0x00), + + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN, 0x45), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE, 0x06), + + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_LVL, 0x24), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x40), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x1E), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_BW, 0x5B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x3F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0D), + + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IVCO, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TRIM, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_PWM_GEAR_BAND, 0x15), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESCODE_DIV_NUM, 0x40), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_MISC2, 0x63), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SVS_SO_GAIN_HALF, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SVS_SO_GAIN, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SO_SATURATION_ENABLE, 0x4B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_INITVAL1, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_INITVAL2, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6c), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0A), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAP, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SYM_RESYNC_CTRL, 0x03), }; static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x54), }; +static struct ufs_qcom_phy_calibration phy_cal_table_rate_B_2_2_0[] = { + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x44), +}; + +/* + * For 2.0.0 revision, apply this SVS mode configuration after PHY power + * up sequence is completed. + */ +static struct ufs_qcom_phy_calibration phy_svs_mode_config_2_0_0[] = { + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x40), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_PWM_GEAR_BAND, 0x15), +}; + #endif diff --git a/drivers/phy/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/phy-qcom-ufs-qmp-20nm.c index b16ea77d07b9..6bc21b6e20bc 100644 --- a/drivers/phy/phy-qcom-ufs-qmp-20nm.c +++ b/drivers/phy/phy-qcom-ufs-qmp-20nm.c @@ -147,6 +147,23 @@ void ufs_qcom_phy_qmp_20nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) mb(); } +static +void ufs_qcom_phy_qmp_20nm_ctrl_rx_linecfg(struct ufs_qcom_phy *phy, bool ctrl) +{ + u32 temp; + + temp = readl_relaxed(phy->mmio + UFS_PHY_LINECFG_DISABLE); + + if (ctrl) /* enable RX LineCfg */ + temp &= ~UFS_PHY_RX_LINECFG_DISABLE_BIT; + else /* disable RX LineCfg */ + temp |= UFS_PHY_RX_LINECFG_DISABLE_BIT; + + writel_relaxed(temp, phy->mmio + UFS_PHY_LINECFG_DISABLE); + /* make sure that RX LineCfg config applied before we return */ + mb(); +} + static inline void ufs_qcom_phy_qmp_20nm_start_serdes(struct ufs_qcom_phy *phy) { u32 tmp; @@ -171,7 +188,7 @@ static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) return err; } -static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { +struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { .init = ufs_qcom_phy_qmp_20nm_init, .exit = ufs_qcom_phy_exit, .power_on = ufs_qcom_phy_power_on, @@ -179,11 +196,12 @@ static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { .owner = THIS_MODULE, }; -static struct ufs_qcom_phy_specific_ops phy_20nm_ops = { +struct ufs_qcom_phy_specific_ops phy_20nm_ops = { .calibrate_phy = ufs_qcom_phy_qmp_20nm_phy_calibrate, .start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes, .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready, .set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable, + .ctrl_rx_linecfg = ufs_qcom_phy_qmp_20nm_ctrl_rx_linecfg, .power_control = ufs_qcom_phy_qmp_20nm_power_control, }; diff --git a/drivers/phy/phy-qcom-ufs-qmp-20nm.h b/drivers/phy/phy-qcom-ufs-qmp-20nm.h index 4f3076bb3d71..cd9203cc923e 100644 --- a/drivers/phy/phy-qcom-ufs-qmp-20nm.h +++ b/drivers/phy/phy-qcom-ufs-qmp-20nm.h @@ -101,6 +101,7 @@ #define UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xE8) #define UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY PHY_OFF(0xFC) #define UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY PHY_OFF(0x100) +#define UFS_PHY_LINECFG_DISABLE PHY_OFF(0x134) #define UFS_PHY_RX_SIGDET_CTRL3 PHY_OFF(0x14c) #define UFS_PHY_RMMI_ATTR_CTRL PHY_OFF(0x160) #define UFS_PHY_RMMI_RX_CFGUPDT_L1 (1 << 7) @@ -118,6 +119,7 @@ #define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x174) #define UFS_PHY_TX_LANE_ENABLE_MASK 0x3 +#define UFS_PHY_RX_LINECFG_DISABLE_BIT BIT(1) /* * This structure represents the 20nm specific phy. diff --git a/drivers/phy/phy-qcom-ufs-qmp-v3-660.c b/drivers/phy/phy-qcom-ufs-qmp-v3-660.c new file mode 100644 index 000000000000..a0cb7d0896d1 --- /dev/null +++ b/drivers/phy/phy-qcom-ufs-qmp-v3-660.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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 "phy-qcom-ufs-qmp-v3-660.h" + +#define UFS_PHY_NAME "ufs_phy_qmp_v3_660" + +static +int ufs_qcom_phy_qmp_v3_660_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, + bool is_rate_B) +{ + int err; + int tbl_size_A, tbl_size_B; + struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; + u8 major = ufs_qcom_phy->host_ctrl_rev_major; + u16 minor = ufs_qcom_phy->host_ctrl_rev_minor; + u16 step = ufs_qcom_phy->host_ctrl_rev_step; + + tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); + tbl_B = phy_cal_table_rate_B; + + if ((major == 0x3) && (minor == 0x001) && (step == 0x001)) { + tbl_A = phy_cal_table_rate_A_3_1_1; + tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_3_1_1); + } else { + dev_err(ufs_qcom_phy->dev, + "%s: Unknown UFS-PHY version (major 0x%x minor 0x%x step 0x%x), no calibration values\n", + __func__, major, minor, step); + err = -ENODEV; + goto out; + } + + err = ufs_qcom_phy_calibrate(ufs_qcom_phy, + tbl_A, tbl_size_A, + tbl_B, tbl_size_B, + is_rate_B); + + if (err) + dev_err(ufs_qcom_phy->dev, + "%s: ufs_qcom_phy_calibrate() failed %d\n", + __func__, err); + +out: + return err; +} + +static int ufs_qcom_phy_qmp_v3_660_init(struct phy *generic_phy) +{ + struct ufs_qcom_phy_qmp_v3_660 *phy = phy_get_drvdata(generic_phy); + struct ufs_qcom_phy *phy_common = &phy->common_cfg; + int err; + + err = ufs_qcom_phy_init_clks(generic_phy, phy_common); + if (err) { + dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_clks() failed %d\n", + __func__, err); + goto out; + } + + err = ufs_qcom_phy_init_vregulators(generic_phy, phy_common); + if (err) { + dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_vregulators() failed %d\n", + __func__, err); + goto out; + } + +out: + return err; +} + +static +void ufs_qcom_phy_qmp_v3_660_power_control(struct ufs_qcom_phy *phy, + bool power_ctrl) +{ + if (!power_ctrl) { + /* apply analog power collapse */ + writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); + /* + * Make sure that PHY knows its analog rail is going to be + * powered OFF. + */ + mb(); + } else { + /* bring PHY out of analog power collapse */ + writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); + + /* + * Before any transactions involving PHY, ensure PHY knows + * that it's analog rail is powered ON. + */ + mb(); + } +} + +static inline +void ufs_qcom_phy_qmp_v3_660_set_tx_lane_enable(struct ufs_qcom_phy *phy, + u32 val) +{ + /* + * v3 PHY does not have TX_LANE_ENABLE register. + * Implement this function so as not to propagate error to caller. + */ +} + +static +void ufs_qcom_phy_qmp_v3_660_ctrl_rx_linecfg(struct ufs_qcom_phy *phy, + bool ctrl) +{ + u32 temp; + + temp = readl_relaxed(phy->mmio + UFS_PHY_LINECFG_DISABLE); + + if (ctrl) /* enable RX LineCfg */ + temp &= ~UFS_PHY_RX_LINECFG_DISABLE_BIT; + else /* disable RX LineCfg */ + temp |= UFS_PHY_RX_LINECFG_DISABLE_BIT; + + writel_relaxed(temp, phy->mmio + UFS_PHY_LINECFG_DISABLE); + /* Make sure that RX LineCfg config applied before we return */ + mb(); +} + +static inline void ufs_qcom_phy_qmp_v3_660_start_serdes( + struct ufs_qcom_phy *phy) +{ + u32 tmp; + + tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); + tmp &= ~MASK_SERDES_START; + tmp |= (1 << OFFSET_SERDES_START); + writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); + /* Ensure register value is committed */ + mb(); +} + +static int ufs_qcom_phy_qmp_v3_660_is_pcs_ready( + struct ufs_qcom_phy *phy_common) +{ + int err = 0; + u32 val; + + err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, + val, (val & MASK_PCS_READY), 10, 1000000); + if (err) + dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", + __func__, err); + return err; +} + +static void ufs_qcom_phy_qmp_v3_660_dbg_register_dump( + struct ufs_qcom_phy *phy) +{ + ufs_qcom_phy_dump_regs(phy, COM_BASE, COM_SIZE, + "PHY QSERDES COM Registers "); + ufs_qcom_phy_dump_regs(phy, PHY_BASE, PHY_SIZE, + "PHY Registers "); + ufs_qcom_phy_dump_regs(phy, RX_BASE, RX_SIZE, + "PHY RX0 Registers "); + ufs_qcom_phy_dump_regs(phy, TX_BASE, TX_SIZE, + "PHY TX0 Registers "); +} + +struct phy_ops ufs_qcom_phy_qmp_v3_660_phy_ops = { + .init = ufs_qcom_phy_qmp_v3_660_init, + .exit = ufs_qcom_phy_exit, + .power_on = ufs_qcom_phy_power_on, + .power_off = ufs_qcom_phy_power_off, + .owner = THIS_MODULE, +}; + +struct ufs_qcom_phy_specific_ops phy_v3_660_ops = { + .calibrate_phy = ufs_qcom_phy_qmp_v3_660_phy_calibrate, + .start_serdes = ufs_qcom_phy_qmp_v3_660_start_serdes, + .is_physical_coding_sublayer_ready = + ufs_qcom_phy_qmp_v3_660_is_pcs_ready, + .set_tx_lane_enable = ufs_qcom_phy_qmp_v3_660_set_tx_lane_enable, + .ctrl_rx_linecfg = ufs_qcom_phy_qmp_v3_660_ctrl_rx_linecfg, + .power_control = ufs_qcom_phy_qmp_v3_660_power_control, + .dbg_register_dump = ufs_qcom_phy_qmp_v3_660_dbg_register_dump, +}; + +static int ufs_qcom_phy_qmp_v3_660_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy *generic_phy; + struct ufs_qcom_phy_qmp_v3_660 *phy; + int err = 0; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) { + err = -ENOMEM; + goto out; + } + + generic_phy = ufs_qcom_phy_generic_probe(pdev, &phy->common_cfg, + &ufs_qcom_phy_qmp_v3_660_phy_ops, + &phy_v3_660_ops); + + if (!generic_phy) { + dev_err(dev, "%s: ufs_qcom_phy_generic_probe() failed\n", + __func__); + err = -EIO; + goto out; + } + + phy_set_drvdata(generic_phy, phy); + + strlcpy(phy->common_cfg.name, UFS_PHY_NAME, + sizeof(phy->common_cfg.name)); + +out: + return err; +} + +static int ufs_qcom_phy_qmp_v3_660_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy *generic_phy = to_phy(dev); + struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); + int err = 0; + + err = ufs_qcom_phy_remove(generic_phy, ufs_qcom_phy); + if (err) + dev_err(dev, "%s: ufs_qcom_phy_remove failed = %d\n", + __func__, err); + + return err; +} + +static const struct of_device_id ufs_qcom_phy_qmp_v3_660_of_match[] = { + {.compatible = "qcom,ufs-phy-qmp-v3-660"}, + {}, +}; +MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_v3_660_of_match); + +static struct platform_driver ufs_qcom_phy_qmp_v3_660_driver = { + .probe = ufs_qcom_phy_qmp_v3_660_probe, + .remove = ufs_qcom_phy_qmp_v3_660_remove, + .driver = { + .of_match_table = ufs_qcom_phy_qmp_v3_660_of_match, + .name = "ufs_qcom_phy_qmp_v3_660", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(ufs_qcom_phy_qmp_v3_660_driver); + +MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP v3 660"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-qcom-ufs-qmp-v3-660.h b/drivers/phy/phy-qcom-ufs-qmp-v3-660.h new file mode 100644 index 000000000000..8d0183d87e20 --- /dev/null +++ b/drivers/phy/phy-qcom-ufs-qmp-v3-660.h @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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. + * + */ + +#ifndef UFS_QCOM_PHY_QMP_V3_660_H_ +#define UFS_QCOM_PHY_QMP_V3_660_H_ + +#include "phy-qcom-ufs-i.h" + +/* QCOM UFS PHY control registers */ +#define COM_BASE 0x000 +#define COM_OFF(x) (COM_BASE + x) +#define COM_SIZE 0x1C0 + +#define TX_BASE 0x400 +#define TX_OFF(x) (TX_BASE + x) +#define TX_SIZE 0x128 + +#define RX_BASE 0x600 +#define RX_OFF(x) (RX_BASE + x) +#define RX_SIZE 0x1FC + +#define PHY_BASE 0xC00 +#define PHY_OFF(x) (PHY_BASE + x) +#define PHY_SIZE 0x1B4 + +/* UFS PHY QSERDES COM registers */ +#define QSERDES_COM_ATB_SEL1 COM_OFF(0x00) +#define QSERDES_COM_ATB_SEL2 COM_OFF(0x04) +#define QSERDES_COM_FREQ_UPDATE COM_OFF(0x08) +#define QSERDES_COM_BG_TIMER COM_OFF(0x0C) +#define QSERDES_COM_SSC_EN_CENTER COM_OFF(0x10) +#define QSERDES_COM_SSC_ADJ_PER1 COM_OFF(0x14) +#define QSERDES_COM_SSC_ADJ_PER2 COM_OFF(0x18) +#define QSERDES_COM_SSC_PER1 COM_OFF(0x1C) +#define QSERDES_COM_SSC_PER2 COM_OFF(0x20) +#define QSERDES_COM_SSC_STEP_SIZE1 COM_OFF(0x24) +#define QSERDES_COM_SSC_STEP_SIZE2 COM_OFF(0x28) +#define QSERDES_COM_POST_DIV COM_OFF(0x2C) +#define QSERDES_COM_POST_DIV_MUX COM_OFF(0x30) +#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x34) +#define QSERDES_COM_CLK_ENABLE1 COM_OFF(0x38) +#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x3C) +#define QSERDES_COM_SYSCLK_BUF_ENABLE COM_OFF(0x40) +#define QSERDES_COM_PLL_EN COM_OFF(0x44) +#define QSERDES_COM_PLL_IVCO COM_OFF(0x48) +#define QSERDES_COM_LOCK_CMP1_MODE0 COM_OFF(0X4C) +#define QSERDES_COM_LOCK_CMP2_MODE0 COM_OFF(0X50) +#define QSERDES_COM_LOCK_CMP3_MODE0 COM_OFF(0X54) +#define QSERDES_COM_LOCK_CMP1_MODE1 COM_OFF(0X58) +#define QSERDES_COM_LOCK_CMP2_MODE1 COM_OFF(0X5C) +#define QSERDES_COM_LOCK_CMP3_MODE1 COM_OFF(0X60) +#define QSERDES_COM_CMD_RSVD0 COM_OFF(0x64) +#define QSERDES_COM_EP_CLOCK_DETECT_CTRL COM_OFF(0x68) +#define QSERDES_COM_SYSCLK_DET_COMP_STATUS COM_OFF(0x6C) +#define QSERDES_COM_BG_TRIM COM_OFF(0x70) +#define QSERDES_COM_CLK_EP_DIV COM_OFF(0x74) +#define QSERDES_COM_CP_CTRL_MODE0 COM_OFF(0x78) +#define QSERDES_COM_CP_CTRL_MODE1 COM_OFF(0x7C) +#define QSERDES_COM_CMN_RSVD1 COM_OFF(0x80) +#define QSERDES_COM_PLL_RCTRL_MODE0 COM_OFF(0x84) +#define QSERDES_COM_PLL_RCTRL_MODE1 COM_OFF(0x88) +#define QSERDES_COM_CMN_RSVD2 COM_OFF(0x8C) +#define QSERDES_COM_PLL_CCTRL_MODE0 COM_OFF(0x90) +#define QSERDES_COM_PLL_CCTRL_MODE1 COM_OFF(0x94) +#define QSERDES_COM_CMN_RSVD3 COM_OFF(0x98) +#define QSERDES_COM_PLL_CNTRL COM_OFF(0x9C) +#define QSERDES_COM_PHASE_SEL_CTRL COM_OFF(0xA0) +#define QSERDES_COM_PHASE_SEL_DC COM_OFF(0xA4) +#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM COM_OFF(0xA8) +#define QSERDES_COM_SYSCLK_EN_SEL COM_OFF(0xAC) +#define QSERDES_COM_CML_SYSCLK_SEL COM_OFF(0xB0) +#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0xB4) +#define QSERDES_COM_RESETSM_CNTRL2 COM_OFF(0xB8) +#define QSERDES_COM_RESTRIM_CTRL COM_OFF(0xBC) +#define QSERDES_COM_RESTRIM_CTRL2 COM_OFF(0xC0) +#define QSERDES_COM_LOCK_CMP_EN COM_OFF(0xC8) +#define QSERDES_COM_LOCK_CMP_CFG COM_OFF(0xCC) +#define QSERDES_COM_DEC_START_MODE0 COM_OFF(0xD0) +#define QSERDES_COM_DEC_START_MODE1 COM_OFF(0xD4) +#define QSERDES_COM_VCOCAL_DEADMAN_CTRL COM_OFF(0xD8) +#define QSERDES_COM_DIV_FRAC_START1_MODE0 COM_OFF(0xDC) +#define QSERDES_COM_DIV_FRAC_START2_MODE0 COM_OFF(0xE0) +#define QSERDES_COM_DIV_FRAC_START3_MODE0 COM_OFF(0xE4) +#define QSERDES_COM_DIV_FRAC_START1_MODE1 COM_OFF(0xE8) +#define QSERDES_COM_DIV_FRAC_START2_MODE1 COM_OFF(0xEC) +#define QSERDES_COM_DIV_FRAC_START3_MODE1 COM_OFF(0xF0) +#define QSERDES_COM_VCO_TUNE_MINVAL1 COM_OFF(0xF4) +#define QSERDES_COM_VCO_TUNE_MINVAL2 COM_OFF(0xF8) +#define QSERDES_COM_CMN_RSVD4 COM_OFF(0xFC) +#define QSERDES_COM_INTEGLOOP_INITVAL COM_OFF(0x100) +#define QSERDES_COM_INTEGLOOP_EN COM_OFF(0x104) +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 COM_OFF(0x108) +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 COM_OFF(0x10C) +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 COM_OFF(0x110) +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 COM_OFF(0x114) +#define QSERDES_COM_VCO_TUNE_MAXVAL1 COM_OFF(0x118) +#define QSERDES_COM_VCO_TUNE_MAXVAL2 COM_OFF(0x11C) +#define QSERDES_COM_RES_TRIM_CONTROL2 COM_OFF(0x120) +#define QSERDES_COM_VCO_TUNE_CTRL COM_OFF(0x124) +#define QSERDES_COM_VCO_TUNE_MAP COM_OFF(0x128) +#define QSERDES_COM_VCO_TUNE1_MODE0 COM_OFF(0x12C) +#define QSERDES_COM_VCO_TUNE2_MODE0 COM_OFF(0x130) +#define QSERDES_COM_VCO_TUNE1_MODE1 COM_OFF(0x134) +#define QSERDES_COM_VCO_TUNE2_MODE1 COM_OFF(0x138) +#define QSERDES_COM_VCO_TUNE_INITVAL1 COM_OFF(0x13C) +#define QSERDES_COM_VCO_TUNE_INITVAL2 COM_OFF(0x140) +#define QSERDES_COM_VCO_TUNE_TIMER1 COM_OFF(0x144) +#define QSERDES_COM_VCO_TUNE_TIMER2 COM_OFF(0x148) +#define QSERDES_COM_SAR COM_OFF(0x14C) +#define QSERDES_COM_SAR_CLK COM_OFF(0x150) +#define QSERDES_COM_SAR_CODE_OUT_STATUS COM_OFF(0x154) +#define QSERDES_COM_SAR_CODE_READY_STATUS COM_OFF(0x158) +#define QSERDES_COM_CMN_STATUS COM_OFF(0x15C) +#define QSERDES_COM_RESET_SM_STATUS COM_OFF(0x160) +#define QSERDES_COM_RESTRIM_CODE_STATUS COM_OFF(0x164) +#define QSERDES_COM_PLLCAL_CODE1_STATUS COM_OFF(0x168) +#define QSERDES_COM_PLLCAL_CODE2_STATUS COM_OFF(0x16C) +#define QSERDES_COM_BG_CTRL COM_OFF(0x170) +#define QSERDES_COM_CLK_SELECT COM_OFF(0x174) +#define QSERDES_COM_HSCLK_SEL COM_OFF(0x178) +#define QSERDES_COM_INTEGLOOP_BINCODE_STATUS COM_OFF(0x17C) +#define QSERDES_COM_PLL_ANALOG COM_OFF(0x180) +#define QSERDES_COM_CORECLK_DIV COM_OFF(0x184) +#define QSERDES_COM_SW_RESET COM_OFF(0x188) +#define QSERDES_COM_CORE_CLK_EN COM_OFF(0x18C) +#define QSERDES_COM_C_READY_STATUS COM_OFF(0x190) +#define QSERDES_COM_CMN_CONFIG COM_OFF(0x194) +#define QSERDES_COM_CMN_RATE_OVERRIDE COM_OFF(0x198) +#define QSERDES_COM_SVS_MODE_CLK_SEL COM_OFF(0x19C) +#define QSERDES_COM_DEBUG_BUS0 COM_OFF(0x1A0) +#define QSERDES_COM_DEBUG_BUS1 COM_OFF(0x1A4) +#define QSERDES_COM_DEBUG_BUS2 COM_OFF(0x1A8) +#define QSERDES_COM_DEBUG_BUS3 COM_OFF(0x1AC) +#define QSERDES_COM_DEBUG_BUS_SEL COM_OFF(0x1B0) +#define QSERDES_COM_CMN_MISC1 COM_OFF(0x1B4) +#define QSERDES_COM_CORECLK_DIV_MODE1 COM_OFF(0x1BC) +#define QSERDES_COM_CMN_RSVD5 COM_OFF(0x1C0) + +/* UFS PHY registers */ +#define UFS_PHY_PHY_START PHY_OFF(0x00) +#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04) +#define UFS_PHY_TX_LARGE_AMP_DRV_LVL PHY_OFF(0x34) +#define UFS_PHY_TX_SMALL_AMP_DRV_LVL PHY_OFF(0x3C) +#define UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAP PHY_OFF(0xCC) +#define UFS_PHY_LINECFG_DISABLE PHY_OFF(0x138) +#define UFS_PHY_RX_SYM_RESYNC_CTRL PHY_OFF(0x13C) +#define UFS_PHY_RX_SIGDET_CTRL2 PHY_OFF(0x148) +#define UFS_PHY_RX_PWM_GEAR_BAND PHY_OFF(0x154) +#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x168) + +/* UFS PHY TX registers */ +#define QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN TX_OFF(0x68) +#define QSERDES_TX_LANE_MODE TX_OFF(0x94) + +/* UFS PHY RX registers */ +#define QSERDES_RX_UCDR_SVS_SO_GAIN_HALF RX_OFF(0x30) +#define QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER RX_OFF(0x34) +#define QSERDES_RX_UCDR_SVS_SO_GAIN_EIGHTH RX_OFF(0x38) +#define QSERDES_RX_UCDR_SVS_SO_GAIN RX_OFF(0x3C) +#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN RX_OFF(0x40) +#define QSERDES_RX_UCDR_SO_SATURATION_ENABLE RX_OFF(0x48) +#define QSERDES_RX_RX_TERM_BW RX_OFF(0x90) +#define QSERDES_RX_RX_EQ_GAIN1_LSB RX_OFF(0xC4) +#define QSERDES_RX_RX_EQ_GAIN1_MSB RX_OFF(0xC8) +#define QSERDES_RX_RX_EQ_GAIN2_LSB RX_OFF(0xCC) +#define QSERDES_RX_RX_EQ_GAIN2_MSB RX_OFF(0xD0) +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(0xD8) +#define QSERDES_RX_SIGDET_CNTRL RX_OFF(0x114) +#define QSERDES_RX_SIGDET_LVL RX_OFF(0x118) +#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL RX_OFF(0x11C) +#define QSERDES_RX_RX_INTERFACE_MODE RX_OFF(0x12C) + + +#define UFS_PHY_RX_LINECFG_DISABLE_BIT BIT(1) + +/* + * This structure represents the v3 660 specific phy. + * common_cfg MUST remain the first field in this structure + * in case extra fields are added. This way, when calling + * get_ufs_qcom_phy() of generic phy, we can extract the + * common phy structure (struct ufs_qcom_phy) out of it + * regardless of the relevant specific phy. + */ +struct ufs_qcom_phy_qmp_v3_660 { + struct ufs_qcom_phy common_cfg; +}; + +static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_1_1[] = { + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x0e), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0x14), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_CFG, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x0b), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x0b), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE1, 0x00), + + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN, 0x45), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE, 0x06), + + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_LVL, 0x24), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x40), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x1E), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_BW, 0x5B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x3F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0D), + + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IVCO, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TRIM, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_PWM_GEAR_BAND, 0x15), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SVS_SO_GAIN_HALF, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SVS_SO_GAIN, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SO_SATURATION_ENABLE, 0x4B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_INITVAL1, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_INITVAL2, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6c), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0A), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAP, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SYM_RESYNC_CTRL, 0x03), +}; + +static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x44), +}; + +#endif diff --git a/drivers/phy/phy-qcom-ufs-qmp-v3.c b/drivers/phy/phy-qcom-ufs-qmp-v3.c new file mode 100644 index 000000000000..a9ad3a6f87cc --- /dev/null +++ b/drivers/phy/phy-qcom-ufs-qmp-v3.c @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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 "phy-qcom-ufs-qmp-v3.h" + +#define UFS_PHY_NAME "ufs_phy_qmp_v3" + +static +int ufs_qcom_phy_qmp_v3_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, + bool is_rate_B) +{ + int err; + int tbl_size_A, tbl_size_B; + struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; + u8 major = ufs_qcom_phy->host_ctrl_rev_major; + u16 minor = ufs_qcom_phy->host_ctrl_rev_minor; + u16 step = ufs_qcom_phy->host_ctrl_rev_step; + + tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); + tbl_B = phy_cal_table_rate_B; + + if ((major == 0x3) && (minor == 0x000) && (step == 0x0000)) { + tbl_A = phy_cal_table_rate_A_3_0_0; + tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_3_0_0); + } else if ((major == 0x3) && (minor == 0x001) && (step == 0x0000)) { + tbl_A = phy_cal_table_rate_A_3_1_0; + tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_3_1_0); + } else { + dev_err(ufs_qcom_phy->dev, + "%s: Unknown UFS-PHY version (major 0x%x minor 0x%x step 0x%x), no calibration values\n", + __func__, major, minor, step); + err = -ENODEV; + goto out; + } + + err = ufs_qcom_phy_calibrate(ufs_qcom_phy, + tbl_A, tbl_size_A, + tbl_B, tbl_size_B, + is_rate_B); + + if (err) + dev_err(ufs_qcom_phy->dev, + "%s: ufs_qcom_phy_calibrate() failed %d\n", + __func__, err); + +out: + return err; +} + +static int ufs_qcom_phy_qmp_v3_init(struct phy *generic_phy) +{ + struct ufs_qcom_phy_qmp_v3 *phy = phy_get_drvdata(generic_phy); + struct ufs_qcom_phy *phy_common = &phy->common_cfg; + int err; + + err = ufs_qcom_phy_init_clks(generic_phy, phy_common); + if (err) { + dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_clks() failed %d\n", + __func__, err); + goto out; + } + + err = ufs_qcom_phy_init_vregulators(generic_phy, phy_common); + if (err) { + dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_vregulators() failed %d\n", + __func__, err); + goto out; + } + +out: + return err; +} + +static +void ufs_qcom_phy_qmp_v3_power_control(struct ufs_qcom_phy *phy, + bool power_ctrl) +{ + if (!power_ctrl) { + /* apply analog power collapse */ + writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); + /* + * Make sure that PHY knows its analog rail is going to be + * powered OFF. + */ + mb(); + } else { + /* bring PHY out of analog power collapse */ + writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); + + /* + * Before any transactions involving PHY, ensure PHY knows + * that it's analog rail is powered ON. + */ + mb(); + } +} + +static inline +void ufs_qcom_phy_qmp_v3_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) +{ + /* + * v3 PHY does not have TX_LANE_ENABLE register. + * Implement this function so as not to propagate error to caller. + */ +} + +static +void ufs_qcom_phy_qmp_v3_ctrl_rx_linecfg(struct ufs_qcom_phy *phy, bool ctrl) +{ + u32 temp; + + temp = readl_relaxed(phy->mmio + UFS_PHY_LINECFG_DISABLE); + + if (ctrl) /* enable RX LineCfg */ + temp &= ~UFS_PHY_RX_LINECFG_DISABLE_BIT; + else /* disable RX LineCfg */ + temp |= UFS_PHY_RX_LINECFG_DISABLE_BIT; + + writel_relaxed(temp, phy->mmio + UFS_PHY_LINECFG_DISABLE); + /* make sure that RX LineCfg config applied before we return */ + mb(); +} + +static inline void ufs_qcom_phy_qmp_v3_start_serdes(struct ufs_qcom_phy *phy) +{ + u32 tmp; + + tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); + tmp &= ~MASK_SERDES_START; + tmp |= (1 << OFFSET_SERDES_START); + writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); + /* Ensure register value is committed */ + mb(); +} + +static int ufs_qcom_phy_qmp_v3_is_pcs_ready(struct ufs_qcom_phy *phy_common) +{ + int err = 0; + u32 val; + + err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, + val, (val & MASK_PCS_READY), 10, 1000000); + if (err) { + dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", + __func__, err); + goto out; + } + +out: + return err; +} + +static +int ufs_qcom_phy_qmp_v3_configure_lpm(struct ufs_qcom_phy *ufs_qcom_phy, + bool enable) +{ + int err = 0; + int tbl_size; + struct ufs_qcom_phy_calibration *tbl = NULL; + + /* The default low power mode configuration is SVS2 */ + if (enable) { + tbl_size = ARRAY_SIZE(phy_cal_table_svs2_enable); + tbl = phy_cal_table_svs2_enable; + } else { + tbl_size = ARRAY_SIZE(phy_cal_table_svs2_disable); + tbl = phy_cal_table_svs2_disable; + } + + if (!tbl) { + dev_err(ufs_qcom_phy->dev, "%s: tbl for SVS2 %s is NULL", + __func__, enable ? "enable" : "disable"); + err = -EINVAL; + goto out; + } + + ufs_qcom_phy_write_tbl(ufs_qcom_phy, tbl, tbl_size); + + /* flush buffered writes */ + mb(); + +out: + return err; +} + +static void ufs_qcom_phy_qmp_v3_dbg_register_dump(struct ufs_qcom_phy *phy) +{ + ufs_qcom_phy_dump_regs(phy, COM_BASE, COM_SIZE, + "PHY QSERDES COM Registers "); + ufs_qcom_phy_dump_regs(phy, PHY_BASE, PHY_SIZE, + "PHY Registers "); + ufs_qcom_phy_dump_regs(phy, RX_BASE(0), RX_SIZE, + "PHY RX0 Registers "); + ufs_qcom_phy_dump_regs(phy, TX_BASE(0), TX_SIZE, + "PHY TX0 Registers "); + ufs_qcom_phy_dump_regs(phy, RX_BASE(1), RX_SIZE, + "PHY RX1 Registers "); + ufs_qcom_phy_dump_regs(phy, TX_BASE(1), TX_SIZE, + "PHY TX1 Registers "); +} + +struct phy_ops ufs_qcom_phy_qmp_v3_phy_ops = { + .init = ufs_qcom_phy_qmp_v3_init, + .exit = ufs_qcom_phy_exit, + .power_on = ufs_qcom_phy_power_on, + .power_off = ufs_qcom_phy_power_off, + .owner = THIS_MODULE, +}; + +struct ufs_qcom_phy_specific_ops phy_v3_ops = { + .calibrate_phy = ufs_qcom_phy_qmp_v3_phy_calibrate, + .start_serdes = ufs_qcom_phy_qmp_v3_start_serdes, + .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_v3_is_pcs_ready, + .set_tx_lane_enable = ufs_qcom_phy_qmp_v3_set_tx_lane_enable, + .ctrl_rx_linecfg = ufs_qcom_phy_qmp_v3_ctrl_rx_linecfg, + .power_control = ufs_qcom_phy_qmp_v3_power_control, + .configure_lpm = ufs_qcom_phy_qmp_v3_configure_lpm, + .dbg_register_dump = ufs_qcom_phy_qmp_v3_dbg_register_dump, +}; + +static int ufs_qcom_phy_qmp_v3_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy *generic_phy; + struct ufs_qcom_phy_qmp_v3 *phy; + int err = 0; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) { + err = -ENOMEM; + goto out; + } + + generic_phy = ufs_qcom_phy_generic_probe(pdev, &phy->common_cfg, + &ufs_qcom_phy_qmp_v3_phy_ops, &phy_v3_ops); + + if (!generic_phy) { + dev_err(dev, "%s: ufs_qcom_phy_generic_probe() failed\n", + __func__); + err = -EIO; + goto out; + } + + phy_set_drvdata(generic_phy, phy); + + strlcpy(phy->common_cfg.name, UFS_PHY_NAME, + sizeof(phy->common_cfg.name)); + +out: + return err; +} + +static int ufs_qcom_phy_qmp_v3_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy *generic_phy = to_phy(dev); + struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); + int err = 0; + + err = ufs_qcom_phy_remove(generic_phy, ufs_qcom_phy); + if (err) + dev_err(dev, "%s: ufs_qcom_phy_remove failed = %d\n", + __func__, err); + + return err; +} + +static const struct of_device_id ufs_qcom_phy_qmp_v3_of_match[] = { + {.compatible = "qcom,ufs-phy-qmp-v3"}, + {}, +}; +MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_v3_of_match); + +static struct platform_driver ufs_qcom_phy_qmp_v3_driver = { + .probe = ufs_qcom_phy_qmp_v3_probe, + .remove = ufs_qcom_phy_qmp_v3_remove, + .driver = { + .of_match_table = ufs_qcom_phy_qmp_v3_of_match, + .name = "ufs_qcom_phy_qmp_v3", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(ufs_qcom_phy_qmp_v3_driver); + +MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP v3"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-qcom-ufs-qmp-v3.h b/drivers/phy/phy-qcom-ufs-qmp-v3.h new file mode 100644 index 000000000000..8cb4b0eeb866 --- /dev/null +++ b/drivers/phy/phy-qcom-ufs-qmp-v3.h @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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. + * + */ + +#ifndef UFS_QCOM_PHY_QMP_V3_H_ +#define UFS_QCOM_PHY_QMP_V3_H_ + +#include "phy-qcom-ufs-i.h" + +/* QCOM UFS PHY control registers */ +#define COM_BASE 0x000 +#define COM_SIZE 0x18C +#define PHY_BASE 0xC00 +#define PHY_SIZE 0x1DC +#define TX_BASE(n) (0x400 + (0x400 * n)) +#define TX_SIZE 0x128 +#define RX_BASE(n) (0x600 + (0x400 * n)) +#define RX_SIZE 0x1FC +#define COM_OFF(x) (COM_BASE + x) +#define PHY_OFF(x) (PHY_BASE + x) +#define TX_OFF(n, x) (TX_BASE(n) + x) +#define RX_OFF(n, x) (RX_BASE(n) + x) + +/* UFS PHY QSERDES COM registers */ +#define QSERDES_COM_ATB_SEL1 COM_OFF(0x00) +#define QSERDES_COM_ATB_SEL2 COM_OFF(0x04) +#define QSERDES_COM_FREQ_UPDATE COM_OFF(0x08) +#define QSERDES_COM_BG_TIMER COM_OFF(0x0C) +#define QSERDES_COM_SSC_EN_CENTER COM_OFF(0x10) +#define QSERDES_COM_SSC_ADJ_PER1 COM_OFF(0x14) +#define QSERDES_COM_SSC_ADJ_PER2 COM_OFF(0x18) +#define QSERDES_COM_SSC_PER1 COM_OFF(0x1C) +#define QSERDES_COM_SSC_PER2 COM_OFF(0x20) +#define QSERDES_COM_SSC_STEP_SIZE1 COM_OFF(0x24) +#define QSERDES_COM_SSC_STEP_SIZE2 COM_OFF(0x28) +#define QSERDES_COM_POST_DIV COM_OFF(0x2C) +#define QSERDES_COM_POST_DIV_MUX COM_OFF(0x30) +#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x34) +#define QSERDES_COM_CLK_ENABLE1 COM_OFF(0x38) +#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x3C) +#define QSERDES_COM_SYSCLK_BUF_ENABLE COM_OFF(0x40) +#define QSERDES_COM_PLL_EN COM_OFF(0x44) +#define QSERDES_COM_PLL_IVCO COM_OFF(0x48) +#define QSERDES_COM_CMN_IETRIM COM_OFF(0x4C) +#define QSERDES_COM_CMN_IPTRIM COM_OFF(0x50) +#define QSERDES_COM_EP_CLOCK_DETECT_CTR COM_OFF(0x54) +#define QSERDES_COM_SYSCLK_DET_COMP_STATUS COM_OFF(0x58) +#define QSERDES_COM_CLK_EP_DIV COM_OFF(0x5C) +#define QSERDES_COM_CP_CTRL_MODE0 COM_OFF(0x60) +#define QSERDES_COM_CP_CTRL_MODE1 COM_OFF(0x64) +#define QSERDES_COM_PLL_RCTRL_MODE0 COM_OFF(0x68) +#define QSERDES_COM_PLL_RCTRL_MODE1 COM_OFF(0x6C) +#define QSERDES_COM_PLL_CCTRL_MODE0 COM_OFF(0x70) +#define QSERDES_COM_PLL_CCTRL_MODE1 COM_OFF(0x74) +#define QSERDES_COM_PLL_CNTRL COM_OFF(0x78) +#define SERDES_COM_BIAS_EN_CTRL_BY_PSM COM_OFF(0x7C) +#define QSERDES_COM_SYSCLK_EN_SEL COM_OFF(0x80) +#define QSERDES_COM_CML_SYSCLK_SEL COM_OFF(0x84) +#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0x88) +#define QSERDES_COM_RESETSM_CNTRL2 COM_OFF(0x8C) +#define QSERDES_COM_LOCK_CMP_EN COM_OFF(0x90) +#define QSERDES_COM_LOCK_CMP_CFG COM_OFF(0x94) +#define QSERDES_COM_LOCK_CMP1_MODE0 COM_OFF(0x98) +#define QSERDES_COM_LOCK_CMP2_MODE0 COM_OFF(0x9C) +#define QSERDES_COM_LOCK_CMP3_MODE0 COM_OFF(0xA0) +#define QSERDES_COM_LOCK_CMP1_MODE1 COM_OFF(0xA4) +#define QSERDES_COM_LOCK_CMP2_MODE1 COM_OFF(0xA8) +#define QSERDES_COM_LOCK_CMP3_MODE1 COM_OFF(0xAC) +#define QSERDES_COM_DEC_START_MODE0 COM_OFF(0xB0) +#define QSERDES_COM_DEC_START_MODE1 COM_OFF(0xB4) +#define QSERDES_COM_DIV_FRAC_START1_MODE0 COM_OFF(0xB8) +#define QSERDES_COM_DIV_FRAC_START2_MODE0 COM_OFF(0xBC) +#define QSERDES_COM_DIV_FRAC_START3_MODE0 COM_OFF(0xC0) +#define QSERDES_COM_DIV_FRAC_START1_MODE1 COM_OFF(0xC4) +#define QSERDES_COM_DIV_FRAC_START2_MODE1 COM_OFF(0xC8) +#define QSERDES_COM_DIV_FRAC_START3_MODE1 COM_OFF(0xCC) +#define QSERDES_COM_INTEGLOOP_INITVAL COM_OFF(0xD0) +#define QSERDES_COM_INTEGLOOP_EN COM_OFF(0xD4) +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 COM_OFF(0xD8) +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 COM_OFF(0xDC) +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 COM_OFF(0xE0) +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 COM_OFF(0xE4) +#define QSERDES_COM_VCOCAL_DEADMAN_CTRL COM_OFF(0xE8) +#define QSERDES_COM_VCO_TUNE_CTRL COM_OFF(0xEC) +#define QSERDES_COM_VCO_TUNE_MAP COM_OFF(0xF0) +#define QSERDES_COM_VCO_TUNE1_MODE0 COM_OFF(0xF4) +#define QSERDES_COM_VCO_TUNE2_MODE0 COM_OFF(0xF8) +#define QSERDES_COM_VCO_TUNE1_MODE1 COM_OFF(0xFC) +#define QSERDES_COM_VCO_TUNE2_MODE1 COM_OFF(0x100) +#define QSERDES_COM_VCO_TUNE_INITVAL1 COM_OFF(0x104) +#define QSERDES_COM_VCO_TUNE_INITVAL2 COM_OFF(0x108) +#define QSERDES_COM_VCO_TUNE_MINVAL1 COM_OFF(0x10C) +#define QSERDES_COM_VCO_TUNE_MINVAL2 COM_OFF(0x110) +#define QSERDES_COM_VCO_TUNE_MAXVAL1 COM_OFF(0x114) +#define QSERDES_COM_VCO_TUNE_MAXVAL2 COM_OFF(0x118) +#define QSERDES_COM_VCO_TUNE_TIMER1 COM_OFF(0x11C) +#define QSERDES_COM_VCO_TUNE_TIMER2 COM_OFF(0x120) +#define QSERDES_COM_CMN_STATUS COM_OFF(0x124) +#define QSERDES_COM_RESET_SM_STATUS COM_OFF(0x128) +#define QSERDES_COM_RESTRIM_CODE_STATUS COM_OFF(0x12C) +#define QSERDES_COM_PLLCAL_CODE1_STATUS COM_OFF(0x130) +#define QSERDES_COM_PLLCAL_CODE2_STATUS COM_OFF(0x134) +#define QSERDES_COM_CLK_SELECT COM_OFF(0x138) +#define QSERDES_COM_HSCLK_SEL COM_OFF(0x13C) +#define QSERDES_COM_INTEGLOOP_BINCODE_STATUS COM_OFF(0x140) +#define QSERDES_COM_PLL_ANALOG COM_OFF(0x144) +#define QSERDES_COM_CORECLK_DIV_MODE0 COM_OFF(0x148) +#define QSERDES_COM_CORECLK_DIV_MODE1 COM_OFF(0x14C) +#define QSERDES_COM_SW_RESET COM_OFF(0x150) +#define QSERDES_COM_CORE_CLK_EN COM_OFF(0x154) +#define QSERDES_COM_C_READY_STATUS COM_OFF(0x158) +#define QSERDES_COM_CMN_CONFIG COM_OFF(0x15C) +#define QSERDES_COM_CMN_RATE_OVERRIDE COM_OFF(0x160) +#define QSERDES_COM_SVS_MODE_CLK_SEL COM_OFF(0x164) +#define QSERDES_COM_DEBUG_BUS0 COM_OFF(0x168) +#define QSERDES_COM_DEBUG_BUS1 COM_OFF(0x16C) +#define QSERDES_COM_DEBUG_BUS2 COM_OFF(0x170) +#define QSERDES_COM_DEBUG_BUS3 COM_OFF(0x174) +#define QSERDES_COM_DEBUG_BUS_SEL COM_OFF(0x178) +#define QSERDES_COM_CMN_MISC1 COM_OFF(0x17C) +#define QSERDES_COM_CMN_MISC2 COM_OFF(0x180) +#define QSERDES_COM_CMN_MODE COM_OFF(0x184) +#define QSERDES_COM_CMN_VREG_SEL COM_OFF(0x188) + +/* UFS PHY registers */ +#define UFS_PHY_PHY_START PHY_OFF(0x00) +#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04) +#define UFS_PHY_TIMER_20US_CORECLK_STEPS_MSB PHY_OFF(0x08) +#define UFS_PHY_TIMER_20US_CORECLK_STEPS_LSB PHY_OFF(0x0C) +#define UFS_PHY_TX_LARGE_AMP_DRV_LVL PHY_OFF(0x2C) +#define UFS_PHY_TX_SMALL_AMP_DRV_LVL PHY_OFF(0x34) +#define UFS_PHY_LINECFG_DISABLE PHY_OFF(0x130) +#define UFS_PHY_RX_SYM_RESYNC_CTRL PHY_OFF(0x134) +#define UFS_PHY_RX_MIN_HIBERN8_TIME PHY_OFF(0x138) +#define UFS_PHY_RX_SIGDET_CTRL1 PHY_OFF(0x13C) +#define UFS_PHY_RX_SIGDET_CTRL2 PHY_OFF(0x140) +#define UFS_PHY_RX_PWM_GEAR_BAND PHY_OFF(0x14C) +#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x160) +#define UFS_PHY_TX_MID_TERM_CTRL1 PHY_OFF(0x1BC) +#define UFS_PHY_MULTI_LANE_CTRL1 PHY_OFF(0x1C4) + +/* UFS PHY TX registers */ +#define QSERDES_TX0_TRANSCEIVER_BIAS_EN TX_OFF(0, 0x5C) +#define QSERDES_TX0_LANE_MODE_1 TX_OFF(0, 0x8C) +#define QSERDES_TX0_LANE_MODE_2 TX_OFF(0, 0x90) +#define QSERDES_TX0_LANE_MODE_3 TX_OFF(0, 0x94) + +#define QSERDES_TX1_LANE_MODE_1 TX_OFF(1, 0x8C) + + +/* UFS PHY RX registers */ +#define QSERDES_RX0_UCDR_SVS_SO_GAIN_HALF RX_OFF(0, 0x24) +#define QSERDES_RX0_UCDR_SVS_SO_GAIN_QUARTER RX_OFF(0, 0x28) +#define QSERDES_RX0_UCDR_SVS_SO_GAIN RX_OFF(0, 0x2C) +#define QSERDES_RX0_UCDR_FASTLOCK_FO_GAIN RX_OFF(0, 0x30) +#define QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE RX_OFF(0, 0x34) +#define QSERDES_RX0_UCDR_FASTLOCK_COUNT_LOW RX_OFF(0, 0x3C) +#define QSERDES_RX0_UCDR_PI_CONTROLS RX_OFF(0, 0x44) +#define QSERDES_RX0_RX_TERM_BW RX_OFF(0, 0x7C) +#define QSERDES_RX0_RX_EQ_GAIN2_LSB RX_OFF(0, 0xC8) +#define QSERDES_RX0_RX_EQ_GAIN2_MSB RX_OFF(0, 0xCC) +#define QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL1 RX_OFF(0, 0xD0) +#define QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(0, 0xD4) +#define QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL3 RX_OFF(0, 0xD8) +#define QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL4 RX_OFF(0, 0xDC) +#define QSERDES_RX0_SIGDET_CNTRL RX_OFF(0, 0x104) +#define QSERDES_RX0_SIGDET_LVL RX_OFF(0, 0x108) +#define QSERDES_RX0_SIGDET_DEGLITCH_CNTRL RX_OFF(0, 0x10C) +#define QSERDES_RX0_RX_INTERFACE_MODE RX_OFF(0, 0x11C) + +#define QSERDES_RX1_UCDR_SVS_SO_GAIN_HALF RX_OFF(1, 0x24) +#define QSERDES_RX1_UCDR_SVS_SO_GAIN_QUARTER RX_OFF(1, 0x28) +#define QSERDES_RX1_UCDR_SVS_SO_GAIN RX_OFF(1, 0x2C) +#define QSERDES_RX1_UCDR_FASTLOCK_FO_GAIN RX_OFF(1, 0x30) +#define QSERDES_RX1_UCDR_SO_SATURATION_AND_ENABLE RX_OFF(1, 0x34) +#define QSERDES_RX1_UCDR_FASTLOCK_COUNT_LOW RX_OFF(1, 0x3C) +#define QSERDES_RX1_UCDR_PI_CONTROLS RX_OFF(1, 0x44) +#define QSERDES_RX1_RX_TERM_BW RX_OFF(1, 0x7C) +#define QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(1, 0xD4) +#define QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL3 RX_OFF(1, 0xD8) +#define QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL4 RX_OFF(1, 0xDC) +#define QSERDES_RX1_SIGDET_CNTRL RX_OFF(1, 0x104) +#define QSERDES_RX1_SIGDET_LVL RX_OFF(1, 0x108) +#define QSERDES_RX1_SIGDET_DEGLITCH_CNTRL RX_OFF(1, 0x10C) +#define QSERDES_RX1_RX_INTERFACE_MODE RX_OFF(1, 0x11C) + +#define UFS_PHY_RX_LINECFG_DISABLE_BIT BIT(1) + +/* + * This structure represents the v3 specific phy. + * common_cfg MUST remain the first field in this structure + * in case extra fields are added. This way, when calling + * get_ufs_qcom_phy() of generic phy, we can extract the + * common phy structure (struct ufs_qcom_phy) out of it + * regardless of the relevant specific phy. + */ +struct ufs_qcom_phy_qmp_v3 { + struct ufs_qcom_phy common_cfg; +}; + +static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_0_0[] = { + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xD5), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0A), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IVCO, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_INITVAL1, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_INITVAL2, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x08), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x34), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x3F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0xCB), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0C), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x08), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x34), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x3F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xB2), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX0_LANE_MODE_1, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_SIGDET_LVL, 0x24), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_SIGDET_CNTRL, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_SIGDET_DEGLITCH_CNTRL, 0x1E), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_INTERFACE_MODE, 0x40), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_FO_GAIN, 0x0B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_TERM_BW, 0x5B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL2, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL3, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL4, 0x1D), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN_HALF, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN_QUARTER, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE, 0x4B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0x81), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_COUNT_LOW, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6E), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0A), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SYM_RESYNC_CTRL, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_MIN_HIBERN8_TIME, 0x9A), /* 8 us */ +}; + +static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_1_0[] = { + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xD5), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0A), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IVCO, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_INITVAL1, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_INITVAL2, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x36), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x3F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0xDA), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0C), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x36), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x3F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xC1), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX0_LANE_MODE_1, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_SIGDET_LVL, 0x24), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_SIGDET_CNTRL, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_SIGDET_DEGLITCH_CNTRL, 0x1E), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_INTERFACE_MODE, 0x40), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_FO_GAIN, 0x0B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_TERM_BW, 0x5B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL2, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL3, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL4, 0x1D), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN_HALF, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN_QUARTER, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE, 0x4B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0x81), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_COUNT_LOW, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX1_LANE_MODE_1, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_SIGDET_LVL, 0x24), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_SIGDET_CNTRL, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_SIGDET_DEGLITCH_CNTRL, 0x1E), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_INTERFACE_MODE, 0x40), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_FASTLOCK_FO_GAIN, 0x0B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_TERM_BW, 0x5B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL2, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL3, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_RX_EQU_ADAPTOR_CNTRL4, 0x1D), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SVS_SO_GAIN_HALF, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SVS_SO_GAIN_QUARTER, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SVS_SO_GAIN, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SO_SATURATION_AND_ENABLE, 0x4B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_PI_CONTROLS, 0x81), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_FASTLOCK_COUNT_LOW, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_MULTI_LANE_CTRL1, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6E), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0A), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SYM_RESYNC_CTRL, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_MID_TERM_CTRL1, 0x43), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL1, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_MIN_HIBERN8_TIME, 0x9A), /* 8 us */ +}; + +static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x44), +}; + +static struct ufs_qcom_phy_calibration phy_cal_table_svs2_enable[] = { + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE0, 0x14), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE1, 0x14), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x7e), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0x7f), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x7e), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x99), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x07), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_MSB, 0x0b), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_LSB, 0x66), +}; + +static struct ufs_qcom_phy_calibration phy_cal_table_svs2_disable[] = { + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE0, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x3f), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_MSB, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_LSB, 0xcc), +}; + +#endif diff --git a/drivers/phy/phy-qcom-ufs-qrbtc-v2.c b/drivers/phy/phy-qcom-ufs-qrbtc-v2.c new file mode 100644 index 000000000000..8836e0ca7020 --- /dev/null +++ b/drivers/phy/phy-qcom-ufs-qrbtc-v2.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2013-2016, Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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 "phy-qcom-ufs-qrbtc-v2.h" + +#define UFS_PHY_NAME "ufs_phy_qrbtc_v2" + +static +int ufs_qcom_phy_qrbtc_v2_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, + bool is_rate_B) +{ + int err; + int tbl_size_A; + struct ufs_qcom_phy_calibration *tbl_A; + struct ufs_qcom_phy_qrbtc_v2 *qrbtc_phy = container_of(ufs_qcom_phy, + struct ufs_qcom_phy_qrbtc_v2, common_cfg); + + writel_relaxed(0x15f, qrbtc_phy->u11_regs + U11_UFS_RESET_REG_OFFSET); + + /* 50ms are required to stabilize the reset */ + usleep_range(50000, 50100); + writel_relaxed(0x0, qrbtc_phy->u11_regs + U11_UFS_RESET_REG_OFFSET); + + /* Set R3PC REF CLK */ + writel_relaxed(0x80, qrbtc_phy->u11_regs + U11_QRBTC_CONTROL_OFFSET); + + + tbl_A = phy_cal_table_rate_A; + tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A); + + err = ufs_qcom_phy_calibrate(ufs_qcom_phy, + tbl_A, tbl_size_A, + NULL, 0, + false); + + if (err) + dev_err(ufs_qcom_phy->dev, + "%s: ufs_qcom_phy_calibrate() failed %d\n", + __func__, err); + + return err; +} + +static int +ufs_qcom_phy_qrbtc_v2_is_pcs_ready(struct ufs_qcom_phy *phy_common) +{ + int err = 0; + u32 val; + struct ufs_qcom_phy_qrbtc_v2 *qrbtc_phy = container_of(phy_common, + struct ufs_qcom_phy_qrbtc_v2, common_cfg); + + /* + * The value we are polling for is 0x3D which represents the + * following masks: + * RESET_SM field: 0x5 + * RESTRIMDONE bit: BIT(3) + * PLLLOCK bit: BIT(4) + * READY bit: BIT(5) + */ + #define QSERDES_COM_RESET_SM_REG_POLL_VAL 0x3D + err = readl_poll_timeout(phy_common->mmio + QSERDES_COM_RESET_SM, + val, (val == QSERDES_COM_RESET_SM_REG_POLL_VAL), 10, 1000000); + + if (err) + dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", + __func__, err); + + writel_relaxed(0x100, qrbtc_phy->u11_regs + U11_QRBTC_TX_CLK_CTRL); + + return err; +} + +static void ufs_qcom_phy_qrbtc_v2_start_serdes(struct ufs_qcom_phy *phy) +{ + u32 temp; + + writel_relaxed(0x01, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL_OFFSET); + + temp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START_OFFSET); + temp |= 0x1; + writel_relaxed(temp, phy->mmio + UFS_PHY_PHY_START_OFFSET); + + /* Ensure register value is committed */ + mb(); +} + +static int ufs_qcom_phy_qrbtc_v2_init(struct phy *generic_phy) +{ + return 0; +} + +struct phy_ops ufs_qcom_phy_qrbtc_v2_phy_ops = { + .init = ufs_qcom_phy_qrbtc_v2_init, + .exit = ufs_qcom_phy_exit, + .owner = THIS_MODULE, +}; + +struct ufs_qcom_phy_specific_ops phy_qrbtc_v2_ops = { + .calibrate_phy = ufs_qcom_phy_qrbtc_v2_phy_calibrate, + .start_serdes = ufs_qcom_phy_qrbtc_v2_start_serdes, + .is_physical_coding_sublayer_ready = + ufs_qcom_phy_qrbtc_v2_is_pcs_ready, +}; + +static int ufs_qcom_phy_qrbtc_v2_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy *generic_phy; + struct ufs_qcom_phy_qrbtc_v2 *phy; + struct resource *res; + int err = 0; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) { + err = -ENOMEM; + goto out; + } + + generic_phy = ufs_qcom_phy_generic_probe(pdev, &phy->common_cfg, + &ufs_qcom_phy_qrbtc_v2_phy_ops, &phy_qrbtc_v2_ops); + + if (!generic_phy) { + dev_err(dev, "%s: ufs_qcom_phy_generic_probe() failed\n", + __func__); + err = -EIO; + goto out; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "u11_user"); + if (!res) { + dev_err(dev, "%s: u11_user resource not found\n", __func__); + err = -EINVAL; + goto out; + } + + phy->u11_regs = devm_ioremap_resource(dev, res); + if (IS_ERR_OR_NULL(phy->u11_regs)) { + if (IS_ERR(phy->u11_regs)) { + err = PTR_ERR(phy->u11_regs); + phy->u11_regs = NULL; + dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n", + __func__, err); + } else { + dev_err(dev, "%s: ioremap for phy_mem resource failed\n", + __func__); + err = -ENOMEM; + } + goto out; + } + + phy_set_drvdata(generic_phy, phy); + + strlcpy(phy->common_cfg.name, UFS_PHY_NAME, + sizeof(phy->common_cfg.name)); + +out: + return err; +} + +static int ufs_qcom_phy_qrbtc_v2_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy *generic_phy = to_phy(dev); + struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); + int err = 0; + + err = ufs_qcom_phy_remove(generic_phy, ufs_qcom_phy); + if (err) + dev_err(dev, "%s: ufs_qcom_phy_remove failed = %d\n", + __func__, err); + + return err; +} + +static const struct of_device_id ufs_qcom_phy_qrbtc_v2_of_match[] = { + {.compatible = "qcom,ufs-phy-qrbtc-v2"}, + {}, +}; +MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qrbtc_v2_of_match); + +static struct platform_driver ufs_qcom_phy_qrbtc_v2_driver = { + .probe = ufs_qcom_phy_qrbtc_v2_probe, + .remove = ufs_qcom_phy_qrbtc_v2_remove, + .driver = { + .of_match_table = ufs_qcom_phy_qrbtc_v2_of_match, + .name = "ufs_qcom_phy_qrbtc_v2", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(ufs_qcom_phy_qrbtc_v2_driver); + +MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QRBTC V2"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-qcom-ufs-qrbtc-v2.h b/drivers/phy/phy-qcom-ufs-qrbtc-v2.h new file mode 100644 index 000000000000..94590e0fd4a0 --- /dev/null +++ b/drivers/phy/phy-qcom-ufs-qrbtc-v2.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2013-2016, Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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. + * + */ + +#ifndef UFS_QCOM_PHY_QRBTC_V2_H_ +#define UFS_QCOM_PHY_QRBTC_V2_H_ + +#include "phy-qcom-ufs-i.h" + +/* QCOM UFS PHY control registers */ +#define COM_OFF(x) (0x000 + x) +#define PHY_OFF(x) (0x700 + x) +#define PHY_USR(x) (x) + +#define UFS_PHY_PHY_START_OFFSET PHY_OFF(0x00) +#define UFS_PHY_POWER_DOWN_CONTROL_OFFSET PHY_OFF(0x04) + +#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN_OFFSET COM_OFF(0x20) +#define QSERDES_COM_SYSCLK_EN_SEL COM_OFF(0x38) +#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x00) +#define QSERDES_COM_RES_CODE_TXBAND COM_OFF(0x3C) +#define QSERDES_COM_PLL_VCOTAIL_EN COM_OFF(0x04) +#define QSERDES_COM_PLL_CNTRL COM_OFF(0x14) +#define QSERDES_COM_PLL_CLKEPDIV COM_OFF(0xB0) +#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0x40) +#define QSERDES_COM_PLL_RXTXEPCLK_EN COM_OFF(0xA8) +#define QSERDES_COM_PLL_CRCTRL COM_OFF(0xAC) +#define QSERDES_COM_DEC_START1 COM_OFF(0x64) +#define QSERDES_COM_DEC_START2 COM_OFF(0xA4) +#define QSERDES_COM_DIV_FRAC_START1 COM_OFF(0x98) +#define QSERDES_COM_DIV_FRAC_START2 COM_OFF(0x9C) +#define QSERDES_COM_DIV_FRAC_START3 COM_OFF(0xA0) +#define QSERDES_COM_PLLLOCK_CMP1 COM_OFF(0x44) +#define QSERDES_COM_PLLLOCK_CMP2 COM_OFF(0x48) +#define QSERDES_COM_PLLLOCK_CMP3 COM_OFF(0x4C) +#define QSERDES_COM_PLLLOCK_CMP_EN COM_OFF(0x50) +#define QSERDES_COM_PLL_IP_SETI COM_OFF(0x18) +#define QSERDES_COM_PLL_CP_SETI COM_OFF(0x24) +#define QSERDES_COM_PLL_IP_SETP COM_OFF(0x28) +#define QSERDES_COM_PLL_CP_SETP COM_OFF(0x2C) +#define QSERDES_COM_RESET_SM COM_OFF(0xBC) +#define QSERDES_COM_PWM_CNTRL1 COM_OFF(0x280) +#define QSERDES_COM_PWM_CNTRL2 COM_OFF(0x284) +#define QSERDES_COM_PWM_NDIV COM_OFF(0x288) +#define QSERDES_COM_CDR_CONTROL COM_OFF(0x200) +#define QSERDES_COM_CDR_CONTROL_HALF COM_OFF(0x298) +#define QSERDES_COM_CDR_CONTROL_QUARTER COM_OFF(0x29C) +#define QSERDES_COM_SIGDET_CNTRL COM_OFF(0x234) +#define QSERDES_COM_SIGDET_CNTRL2 COM_OFF(0x28C) +#define QSERDES_COM_UFS_CNTRL COM_OFF(0x290) + +/* QRBTC V2 USER REGISTERS */ +#define U11_UFS_RESET_REG_OFFSET PHY_USR(0x4) +#define U11_QRBTC_CONTROL_OFFSET PHY_USR(0x18) +#define U11_QRBTC_TX_CLK_CTRL PHY_USR(0x20) + +static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = { + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_PHY_START_OFFSET, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN_OFFSET, 0x3F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_TXBAND, 0xC0), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x88), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x30), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0x94), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x98), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x8C), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0xAE), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x1F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xF7), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x13), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x3B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x0A), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x04), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PWM_CNTRL1, 0xCF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PWM_CNTRL2, 0x61), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PWM_NDIV, 0x4F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CDR_CONTROL, 0xF2), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CDR_CONTROL_HALF, 0x2A), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CDR_CONTROL_QUARTER, 0x2A), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SIGDET_CNTRL, 0xC0), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SIGDET_CNTRL2, 0x07), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_UFS_CNTRL, 0x18), +}; + +/* + * This structure represents the qrbtc-v2 specific phy. + * common_cfg MUST remain the first field in this structure + * in case extra fields are added. This way, when calling + * get_ufs_qcom_phy() of generic phy, we can extract the + * common phy structure (struct ufs_qcom_phy) out of it + * regardless of the relevant specific phy. + */ +struct ufs_qcom_phy_qrbtc_v2 { + struct ufs_qcom_phy common_cfg; + void __iomem *u11_regs; +}; + +#endif diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c index 107cb57c3513..1e3e175d3e8d 100644 --- a/drivers/phy/phy-qcom-ufs.c +++ b/drivers/phy/phy-qcom-ufs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. + * Copyright (c) 2013-2016, Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -15,9 +15,9 @@ #include "phy-qcom-ufs-i.h" #define MAX_PROP_NAME 32 -#define VDDA_PHY_MIN_UV 1000000 -#define VDDA_PHY_MAX_UV 1000000 -#define VDDA_PLL_MIN_UV 1800000 +#define VDDA_PHY_MIN_UV 800000 +#define VDDA_PHY_MAX_UV 925000 +#define VDDA_PLL_MIN_UV 1200000 #define VDDA_PLL_MAX_UV 1800000 #define VDDP_REF_CLK_MIN_UV 1200000 #define VDDP_REF_CLK_MAX_UV 1200000 @@ -29,13 +29,24 @@ static int ufs_qcom_phy_init_vreg(struct phy *, struct ufs_qcom_phy_vreg *, static int ufs_qcom_phy_base_init(struct platform_device *pdev, struct ufs_qcom_phy *phy_common); +void ufs_qcom_phy_write_tbl(struct ufs_qcom_phy *ufs_qcom_phy, + struct ufs_qcom_phy_calibration *tbl, + int tbl_size) +{ + int i; + + for (i = 0; i < tbl_size; i++) + writel_relaxed(tbl[i].cfg_value, + ufs_qcom_phy->mmio + tbl[i].reg_offset); +} +EXPORT_SYMBOL(ufs_qcom_phy_write_tbl); + int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, struct ufs_qcom_phy_calibration *tbl_A, int tbl_size_A, struct ufs_qcom_phy_calibration *tbl_B, int tbl_size_B, bool is_rate_B) { - int i; int ret = 0; if (!tbl_A) { @@ -44,9 +55,7 @@ int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, goto out; } - for (i = 0; i < tbl_size_A; i++) - writel_relaxed(tbl_A[i].cfg_value, - ufs_qcom_phy->mmio + tbl_A[i].reg_offset); + ufs_qcom_phy_write_tbl(ufs_qcom_phy, tbl_A, tbl_size_A); /* * In case we would like to work in rate B, we need @@ -62,9 +71,7 @@ int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, goto out; } - for (i = 0; i < tbl_size_B; i++) - writel_relaxed(tbl_B[i].cfg_value, - ufs_qcom_phy->mmio + tbl_B[i].reg_offset); + ufs_qcom_phy_write_tbl(ufs_qcom_phy, tbl_B, tbl_size_B); } /* flush buffered writes */ @@ -106,6 +113,14 @@ struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, goto out; } + /* + * UFS PHY power management is managed by its parent (UFS host + * controller) hence set the no the no runtime PM callbacks flag + * on UFS PHY device to avoid any accidental attempt to call the + * PM callbacks for PHY device. + */ + pm_runtime_no_callbacks(&generic_phy->dev); + common_cfg->phy_spec_ops = phy_spec_ops; common_cfg->dev = dev; @@ -135,23 +150,21 @@ int ufs_qcom_phy_base_init(struct platform_device *pdev, int err = 0; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem"); + if (!res) { + dev_err(dev, "%s: phy_mem resource not found\n", __func__); + err = -ENOMEM; + goto out; + } + phy_common->mmio = devm_ioremap_resource(dev, res); if (IS_ERR((void const *)phy_common->mmio)) { err = PTR_ERR((void const *)phy_common->mmio); phy_common->mmio = NULL; dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n", __func__, err); - return err; } - - /* "dev_ref_clk_ctrl_mem" is optional resource */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "dev_ref_clk_ctrl_mem"); - phy_common->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res); - if (IS_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio)) - phy_common->dev_ref_clk_ctrl_mmio = NULL; - - return 0; +out: + return err; } static int __ufs_qcom_phy_clk_get(struct phy *phy, @@ -187,15 +200,19 @@ ufs_qcom_phy_init_clks(struct phy *generic_phy, { int err; - err = ufs_qcom_phy_clk_get(generic_phy, "tx_iface_clk", - &phy_common->tx_iface_clk); - if (err) - goto out; + /* + * tx_iface_clk does not exist in newer version of ufs-phy HW, + * so don't return error if it is not found + */ + __ufs_qcom_phy_clk_get(generic_phy, "tx_iface_clk", + &phy_common->tx_iface_clk, false); - err = ufs_qcom_phy_clk_get(generic_phy, "rx_iface_clk", - &phy_common->rx_iface_clk); - if (err) - goto out; + /* + * rx_iface_clk does not exist in newer version of ufs-phy HW, + * so don't return error if it is not found + */ + __ufs_qcom_phy_clk_get(generic_phy, "rx_iface_clk", + &phy_common->rx_iface_clk, false); err = ufs_qcom_phy_clk_get(generic_phy, "ref_clk_src", &phy_common->ref_clk_src); @@ -211,7 +228,15 @@ ufs_qcom_phy_init_clks(struct phy *generic_phy, err = ufs_qcom_phy_clk_get(generic_phy, "ref_clk", &phy_common->ref_clk); + if (err) + goto out; + /* + * "ref_aux_clk" is optional and only supported by certain + * phy versions, don't abort init if it's not found. + */ + __ufs_qcom_phy_clk_get(generic_phy, "ref_aux_clk", + &phy_common->ref_aux_clk, false); out: return err; } @@ -230,7 +255,6 @@ ufs_qcom_phy_init_vregulators(struct phy *generic_phy, err = ufs_qcom_phy_init_vreg(generic_phy, &phy_common->vdda_phy, "vdda-phy"); - if (err) goto out; @@ -251,6 +275,14 @@ static int __ufs_qcom_phy_init_vreg(struct phy *phy, char prop_name[MAX_PROP_NAME]; + if (dev->of_node) { + snprintf(prop_name, MAX_PROP_NAME, "%s-supply", name); + if (!of_parse_phandle(dev->of_node, prop_name, 0)) { + dev_dbg(dev, "No vreg data found for %s\n", prop_name); + return optional ? err : -ENODATA; + } + } + vreg->name = kstrdup(name, GFP_KERNEL); if (!vreg->name) { err = -ENOMEM; @@ -421,9 +453,26 @@ int ufs_qcom_phy_enable_ref_clk(struct phy *generic_phy) goto out_disable_parent; } + /* + * "ref_aux_clk" is optional clock and only supported by certain + * phy versions, hence make sure that clk reference is available + * before trying to enable the clock. + */ + if (phy->ref_aux_clk) { + ret = clk_prepare_enable(phy->ref_aux_clk); + if (ret) { + dev_err(phy->dev, "%s: ref_aux_clk enable failed %d\n", + __func__, ret); + goto out_disable_ref; + } + } + phy->is_ref_clk_enabled = true; goto out; +out_disable_ref: + if (phy->ref_clk) + clk_disable_unprepare(phy->ref_clk); out_disable_parent: if (phy->ref_clk_parent) clk_disable_unprepare(phy->ref_clk_parent); @@ -464,6 +513,13 @@ void ufs_qcom_phy_disable_ref_clk(struct phy *generic_phy) struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy); if (phy->is_ref_clk_enabled) { + /* + * "ref_aux_clk" is optional clock and only supported by + * certain phy versions, hence make sure that clk reference + * is available before trying to disable the clock. + */ + if (phy->ref_aux_clk) + clk_disable_unprepare(phy->ref_aux_clk); clk_disable_unprepare(phy->ref_clk); /* * "ref_clk_parent" is optional clock hence make sure that clk @@ -477,56 +533,6 @@ void ufs_qcom_phy_disable_ref_clk(struct phy *generic_phy) } EXPORT_SYMBOL_GPL(ufs_qcom_phy_disable_ref_clk); -#define UFS_REF_CLK_EN (1 << 5) - -static void ufs_qcom_phy_dev_ref_clk_ctrl(struct phy *generic_phy, bool enable) -{ - struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy); - - if (phy->dev_ref_clk_ctrl_mmio && - (enable ^ phy->is_dev_ref_clk_enabled)) { - u32 temp = readl_relaxed(phy->dev_ref_clk_ctrl_mmio); - - if (enable) - temp |= UFS_REF_CLK_EN; - else - temp &= ~UFS_REF_CLK_EN; - - /* - * If we are here to disable this clock immediately after - * entering into hibern8, we need to make sure that device - * ref_clk is active atleast 1us after the hibern8 enter. - */ - if (!enable) - udelay(1); - - writel_relaxed(temp, phy->dev_ref_clk_ctrl_mmio); - /* ensure that ref_clk is enabled/disabled before we return */ - wmb(); - /* - * If we call hibern8 exit after this, we need to make sure that - * device ref_clk is stable for atleast 1us before the hibern8 - * exit command. - */ - if (enable) - udelay(1); - - phy->is_dev_ref_clk_enabled = enable; - } -} - -void ufs_qcom_phy_enable_dev_ref_clk(struct phy *generic_phy) -{ - ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, true); -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_enable_dev_ref_clk); - -void ufs_qcom_phy_disable_dev_ref_clk(struct phy *generic_phy) -{ - ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, false); -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_disable_dev_ref_clk); - /* Turn ON M-PHY RMMI interface clocks */ int ufs_qcom_phy_enable_iface_clk(struct phy *generic_phy) { @@ -536,6 +542,9 @@ int ufs_qcom_phy_enable_iface_clk(struct phy *generic_phy) if (phy->is_iface_clk_enabled) goto out; + if (!phy->tx_iface_clk) + goto out; + ret = clk_prepare_enable(phy->tx_iface_clk); if (ret) { dev_err(phy->dev, "%s: tx_iface_clk enable failed %d\n", @@ -561,6 +570,9 @@ void ufs_qcom_phy_disable_iface_clk(struct phy *generic_phy) { struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy); + if (!phy->tx_iface_clk) + return; + if (phy->is_iface_clk_enabled) { clk_disable_unprepare(phy->tx_iface_clk); clk_disable_unprepare(phy->rx_iface_clk); @@ -591,19 +603,26 @@ int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes) struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); int ret = 0; - if (!ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable) { - dev_err(ufs_qcom_phy->dev, "%s: set_tx_lane_enable() callback is not supported\n", - __func__); - ret = -ENOTSUPP; - } else { + if (ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable) ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable(ufs_qcom_phy, tx_lanes); - } return ret; } EXPORT_SYMBOL_GPL(ufs_qcom_phy_set_tx_lane_enable); +int ufs_qcom_phy_ctrl_rx_linecfg(struct phy *generic_phy, bool ctrl) +{ + struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); + int ret = 0; + + if (ufs_qcom_phy->phy_spec_ops->ctrl_rx_linecfg) + ufs_qcom_phy->phy_spec_ops->ctrl_rx_linecfg(ufs_qcom_phy, ctrl); + + return ret; +} +EXPORT_SYMBOL_GPL(ufs_qcom_phy_ctrl_rx_linecfg); + void ufs_qcom_phy_save_controller_version(struct phy *generic_phy, u8 major, u16 minor, u16 step) { @@ -636,6 +655,14 @@ int ufs_qcom_phy_calibrate_phy(struct phy *generic_phy, bool is_rate_B) } EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate_phy); +const char *ufs_qcom_phy_name(struct phy *phy) +{ + struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy); + + return ufs_qcom_phy->name; +} +EXPORT_SYMBOL(ufs_qcom_phy_name); + int ufs_qcom_phy_remove(struct phy *generic_phy, struct ufs_qcom_phy *ufs_qcom_phy) { @@ -747,3 +774,39 @@ int ufs_qcom_phy_power_off(struct phy *generic_phy) return 0; } EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off); + +int ufs_qcom_phy_configure_lpm(struct phy *generic_phy, bool enable) +{ + struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); + int ret = 0; + + if (ufs_qcom_phy->phy_spec_ops->configure_lpm) { + ret = ufs_qcom_phy->phy_spec_ops-> + configure_lpm(ufs_qcom_phy, enable); + if (ret) + dev_err(ufs_qcom_phy->dev, + "%s: configure_lpm(%s) failed %d\n", + __func__, enable ? "enable" : "disable", ret); + } + + return ret; +} +EXPORT_SYMBOL(ufs_qcom_phy_configure_lpm); + +void ufs_qcom_phy_dump_regs(struct ufs_qcom_phy *phy, int offset, + int len, char *prefix) +{ + print_hex_dump(KERN_ERR, prefix, + len > 4 ? DUMP_PREFIX_OFFSET : DUMP_PREFIX_NONE, + 16, 4, phy->mmio + offset, len, false); +} +EXPORT_SYMBOL(ufs_qcom_phy_dump_regs); + +void ufs_qcom_phy_dbg_register_dump(struct phy *generic_phy) +{ + struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); + + if (ufs_qcom_phy->phy_spec_ops->dbg_register_dump) + ufs_qcom_phy->phy_spec_ops->dbg_register_dump(ufs_qcom_phy); +} +EXPORT_SYMBOL(ufs_qcom_phy_dbg_register_dump); |
