diff options
author | Linaro CI <ci_notify@linaro.org> | 2018-06-17 10:13:07 +0000 |
---|---|---|
committer | Linaro CI <ci_notify@linaro.org> | 2018-06-17 10:13:07 +0000 |
commit | e6e232434425139e0cdbe8652f2fdcbefd87a3b7 (patch) | |
tree | 7370e44066d432ee1998382639a2b8602cdf64d3 | |
parent | bfeca2197f83971bb608eeaac637e1ccc11ed7ba (diff) | |
parent | 25a2232383d1a9bedc37342aa7ed54ebde4526d7 (diff) |
Merge remote-tracking branch 'ufs/qcomlt/ufs' into integration-linux-qcomlt
-rw-r--r-- | Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt | 1 | ||||
-rw-r--r-- | drivers/phy/qualcomm/phy-qcom-qmp.c | 122 | ||||
-rw-r--r-- | drivers/phy/qualcomm/phy-qcom-qmp.h | 8 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufs-qcom.c | 172 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufs-qcom.h | 5 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufshcd.h | 13 |
6 files changed, 243 insertions, 78 deletions
diff --git a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt index 266a1bb8bb6e..5ab216f9ece8 100644 --- a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt +++ b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt @@ -11,6 +11,7 @@ Required properties: "qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996, "qcom,sdm845-qmp-usb3-phy" for USB3 QMP V3 phy on sdm845, "qcom,sdm845-qmp-usb3-uni-phy" for USB3 QMP V3 UNI phy on sdm845. + "qcom,sdm845-qmp-ufs-phy" for UFS PHY on SDM845 - reg: offset and length of register set for PHY's common serdes block. diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 4c470104a0d6..ee40329580ac 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -156,6 +156,11 @@ static const unsigned int qmp_v3_usb3phy_regs_layout[] = { [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170, }; +static const unsigned int ufsphy_regs_layout[] = { + [QPHY_START_CTRL] = 0x00, + [QPHY_PCS_READY_STATUS] = 0x168, +}; + static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c), QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), @@ -601,6 +606,73 @@ static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V3_PCS_REFGEN_REQ_CONFIG2, 0x60), }; +static const struct qmp_phy_init_tbl ufsphy_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_CTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_INITVAL1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_INITVAL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xda), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE1, 0x98), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE1, 0xc1), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE1, 0x32), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE1, 0x0f), + + /*Rate B*/ + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x44), +}; + +static const struct qmp_phy_init_tbl ufsphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06), +}; + +static const struct qmp_phy_init_tbl ufsphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_LVL, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_TERM_BW, 0x5b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1d), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN_QUARTER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x81), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x80), +}; + +static const struct qmp_phy_init_tbl ufsphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_DOWN_CONTROL, 0x01), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_MULTI_LANE_CTRL1, 0x02), +}; /* struct qmp_phy_cfg - per-PHY initialization config */ struct qmp_phy_cfg { @@ -652,6 +724,9 @@ struct qmp_phy_cfg { /* Register offset of secondary tx/rx lanes for USB DP combo PHY */ unsigned int tx_b_lane_offset; unsigned int rx_b_lane_offset; + + /* true, if PCS block has a separate SW_RESET register */ + bool has_sw_rst; }; /** @@ -748,6 +823,10 @@ static const char * const qmp_v3_phy_clk_l[] = { "aux", "cfg_ahb", "ref", "com_aux", }; +static const char * const sdm845_ufs_phy_clk_l[] = { + "ref_clk", "ref_aux_clk", +}; + /* list of resets */ static const char * const msm8996_pciephy_reset_l[] = { "phy", "common", "cfg", @@ -791,6 +870,7 @@ static const struct qmp_phy_cfg msm8996_pciephy_cfg = { .has_pwrdn_delay = true, .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, + .has_sw_rst = true, }; static const struct qmp_phy_cfg msm8996_usb3phy_cfg = { @@ -816,6 +896,7 @@ static const struct qmp_phy_cfg msm8996_usb3phy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, .mask_pcs_ready = PHYSTATUS, + .has_sw_rst = true, }; /* list of resets */ @@ -852,6 +933,7 @@ static const struct qmp_phy_cfg ipq8074_pciephy_cfg = { .has_pwrdn_delay = true, .pwrdn_delay_min = 995, /* us */ .pwrdn_delay_max = 1005, /* us */ + .has_sw_rst = true, }; static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = { @@ -885,6 +967,30 @@ static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = { .has_phy_dp_com_ctrl = true, .tx_b_lane_offset = 0x400, .rx_b_lane_offset = 0x400, + .has_sw_rst = true, +}; + +static const struct qmp_phy_cfg sdm845_ufsphy_cfg = { + .type = PHY_TYPE_UFS, + .nlanes = 2, + + .serdes_tbl = ufsphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(ufsphy_serdes_tbl), + .tx_tbl = ufsphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(ufsphy_tx_tbl), + .rx_tbl = ufsphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(ufsphy_rx_tbl), + .pcs_tbl = ufsphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(ufsphy_pcs_tbl), + .clk_list = sdm845_ufs_phy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l), + .vreg_list = msm8996_phy_vreg_l, + .num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l), + .regs = ufsphy_regs_layout, + + .start_ctrl = SERDES_START, + .pwrdn_ctrl = SW_PWRDN, + .mask_pcs_ready = PCS_READY, }; static const struct qmp_phy_cfg qmp_v3_usb3_uniphy_cfg = { @@ -1008,7 +1114,9 @@ static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp) void __iomem *status; unsigned int mask, val; - qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], SW_RESET); + if (cfg->has_sw_rst) + qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], + SW_RESET); qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL], SERDES_START | PCS_START); @@ -1056,7 +1164,8 @@ static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp) if (cfg->has_phy_com_ctrl) { qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL], SERDES_START | PCS_START); - qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], + if (cfg->has_sw_rst) + qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], SW_RESET); qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL], SW_PWRDN); @@ -1133,7 +1242,8 @@ static int qcom_qmp_phy_init(struct phy *phy) usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max); /* Pull PHY out of reset state */ - qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); + if (cfg->has_sw_rst) + qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); if (cfg->has_phy_dp_com_ctrl) qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET); @@ -1173,7 +1283,8 @@ static int qcom_qmp_phy_exit(struct phy *phy) clk_disable_unprepare(qphy->pipe_clk); /* PHY reset */ - qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); + if (cfg->has_sw_rst) + qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); /* stop SerDes and Phy-Coding-Sublayer */ qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); @@ -1522,6 +1633,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }, { .compatible = "qcom,sdm845-qmp-usb3-uni-phy", .data = &qmp_v3_usb3_uniphy_cfg, + }, { + .compatible = "qcom,sdm845-qmp-ufs-phy", + .data = &sdm845_ufsphy_cfg, }, { }, }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index 5d78d43ba9fc..3cbcfe1b75f2 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -184,6 +184,8 @@ #define QSERDES_V3_COM_VCO_TUNE2_MODE0 0x0f8 #define QSERDES_V3_COM_VCO_TUNE1_MODE1 0x0fc #define QSERDES_V3_COM_VCO_TUNE2_MODE1 0x100 +#define QSERDES_V3_COM_VCO_TUNE_INITVAL1 0x104 +#define QSERDES_V3_COM_VCO_TUNE_INITVAL2 0x108 #define QSERDES_V3_COM_VCO_TUNE_TIMER1 0x11c #define QSERDES_V3_COM_VCO_TUNE_TIMER2 0x120 #define QSERDES_V3_COM_CLK_SELECT 0x138 @@ -211,8 +213,13 @@ /* Only for QMP V3 PHY - RX registers */ #define QSERDES_V3_RX_UCDR_SO_GAIN_HALF 0x00c #define QSERDES_V3_RX_UCDR_SO_GAIN 0x014 +#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF 0x024 +#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_QUARTER 0x028 +#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN 0x02c #define QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN 0x030 #define QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034 +#define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c +#define QSERDES_V3_RX_UCDR_PI_CONTROLS 0x044 #define QSERDES_V3_RX_RX_TERM_BW 0x07c #define QSERDES_V3_RX_VGA_CAL_CNTRL1 0x0bc #define QSERDES_V3_RX_VGA_CAL_CNTRL2 0x0c0 @@ -275,6 +282,7 @@ #define QPHY_V3_PCS_FLL_CNT_VAL_L 0x0cc #define QPHY_V3_PCS_FLL_CNT_VAL_H_TOL 0x0d0 #define QPHY_V3_PCS_FLL_MAN_CODE 0x0d4 +#define QPHY_V3_PCS_MULTI_LANE_CTRL1 0x1c4 #define QPHY_V3_PCS_RX_SIGDET_LVL 0x1d8 #define QPHY_V3_PCS_REFGEN_REQ_CONFIG1 0x20c #define QPHY_V3_PCS_REFGEN_REQ_CONFIG2 0x210 diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 221820a7c78b..60ad6f5bec86 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -16,7 +16,6 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/phy/phy.h> -#include <linux/phy/phy-qcom-ufs.h> #include "ufshcd.h" #include "ufshcd-pltfrm.h" @@ -113,10 +112,10 @@ static void ufs_qcom_disable_lane_clks(struct ufs_qcom_host *host) if (!host->is_lane_clks_enabled) return; - if (host->hba->lanes_per_direction > 1) + if (host->tx_l1_sync_clk) clk_disable_unprepare(host->tx_l1_sync_clk); clk_disable_unprepare(host->tx_l0_sync_clk); - if (host->hba->lanes_per_direction > 1) + if (host->rx_l1_sync_clk) clk_disable_unprepare(host->rx_l1_sync_clk); clk_disable_unprepare(host->rx_l0_sync_clk); @@ -147,18 +146,15 @@ static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host *host) if (err) goto disable_tx_l0; - err = ufs_qcom_host_clk_enable(dev, "tx_lane1_sync_clk", - host->tx_l1_sync_clk); - if (err) - goto disable_rx_l1; + /* The tx lane1 clk could be muxed, hence keep this optional */ + if (host->tx_l1_sync_clk) + ufs_qcom_host_clk_enable(dev, "tx_lane1_sync_clk", + host->tx_l1_sync_clk); } host->is_lane_clks_enabled = true; goto out; -disable_rx_l1: - if (host->hba->lanes_per_direction > 1) - clk_disable_unprepare(host->rx_l1_sync_clk); disable_tx_l0: clk_disable_unprepare(host->tx_l0_sync_clk); disable_rx_l0: @@ -189,8 +185,9 @@ static int ufs_qcom_init_lane_clks(struct ufs_qcom_host *host) if (err) goto out; - err = ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk", - &host->tx_l1_sync_clk); + /* The tx lane1 clk could be muxed, hence keep this optional */ + ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk", + &host->tx_l1_sync_clk); } out: return err; @@ -198,21 +195,11 @@ out: static int ufs_qcom_link_startup_post_change(struct ufs_hba *hba) { - struct ufs_qcom_host *host = ufshcd_get_variant(hba); - struct phy *phy = host->generic_phy; u32 tx_lanes; int err = 0; err = ufs_qcom_get_connected_tx_lanes(hba, &tx_lanes); - if (err) - goto out; - err = ufs_qcom_phy_set_tx_lane_enable(phy, tx_lanes); - if (err) - dev_err(hba->dev, "%s: ufs_qcom_phy_set_tx_lane_enable failed\n", - __func__); - -out: return err; } @@ -268,13 +255,16 @@ static void ufs_qcom_select_unipro_mode(struct ufs_qcom_host *host) static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) { struct ufs_qcom_host *host = ufshcd_get_variant(hba); - struct phy *phy = host->generic_phy; + struct phy *phy_lane0 = host->phy_lane0; + struct phy *phy_lane1 = host->phy_lane1; int ret = 0; bool is_rate_B = (UFS_QCOM_LIMIT_HS_RATE == PA_HS_MODE_B) ? true : false; - if (is_rate_B) - phy_set_mode(phy, PHY_MODE_UFS_HS_B); + if (is_rate_B) { + phy_set_mode(phy_lane0, PHY_MODE_UFS_HS_B); + phy_set_mode(phy_lane1, PHY_MODE_UFS_HS_B); + } /* Assert PHY reset and apply PHY calibration values */ ufs_qcom_assert_reset(hba); @@ -282,9 +272,16 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) usleep_range(1000, 1100); /* phy initialization - calibrate the phy */ - ret = phy_init(phy); + ret = phy_init(phy_lane0); if (ret) { - dev_err(hba->dev, "%s: phy init failed, ret = %d\n", + dev_err(hba->dev, "%s: phy lane0 init failed, ret = %d\n", + __func__, ret); + goto out; + } + + ret = phy_init(phy_lane1); + if (ret) { + dev_err(hba->dev, "%s: phy lane1 init failed, ret = %d\n", __func__, ret); goto out; } @@ -299,9 +296,16 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) usleep_range(1000, 1100); /* power on phy - start serdes and phy's power and clocks */ - ret = phy_power_on(phy); + ret = phy_power_on(phy_lane0); + if (ret) { + dev_err(hba->dev, "%s: phy lane0 power on failed, ret = %d\n", + __func__, ret); + goto out_disable_phy; + } + + ret = phy_power_on(phy_lane1); if (ret) { - dev_err(hba->dev, "%s: phy power on failed, ret = %d\n", + dev_err(hba->dev, "%s: phy lane1 power on failed, ret = %d\n", __func__, ret); goto out_disable_phy; } @@ -312,7 +316,8 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) out_disable_phy: ufs_qcom_assert_reset(hba); - phy_exit(phy); + phy_exit(phy_lane0); + phy_exit(phy_lane1); out: return ret; } @@ -563,7 +568,8 @@ out: static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) { struct ufs_qcom_host *host = ufshcd_get_variant(hba); - struct phy *phy = host->generic_phy; + struct phy *phy_lane0 = host->phy_lane0; + struct phy *phy_lane1 = host->phy_lane1; int ret = 0; if (ufs_qcom_is_link_off(hba)) { @@ -573,7 +579,8 @@ static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) * after downstream clocks are disabled. */ ufs_qcom_disable_lane_clks(host); - phy_power_off(phy); + phy_power_off(phy_lane0); + phy_power_off(phy_lane1); /* Assert PHY soft reset */ ufs_qcom_assert_reset(hba); @@ -586,7 +593,8 @@ static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) */ if (!ufs_qcom_is_link_active(hba)) { ufs_qcom_disable_lane_clks(host); - phy_power_off(phy); + phy_power_off(phy_lane0); + phy_power_off(phy_lane1); } out: @@ -596,12 +604,20 @@ out: static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) { struct ufs_qcom_host *host = ufshcd_get_variant(hba); - struct phy *phy = host->generic_phy; + struct phy *phy_lane0 = host->phy_lane0; + struct phy *phy_lane1 = host->phy_lane1; int err; - err = phy_power_on(phy); + err = phy_power_on(phy_lane0); + if (err) { + dev_err(hba->dev, "%s: failed enabling phy lane0 regs, err = %d\n", + __func__, err); + goto out; + } + + err = phy_power_on(phy_lane1); if (err) { - dev_err(hba->dev, "%s: failed enabling regs, err = %d\n", + dev_err(hba->dev, "%s: failed enabling phy lane1 regs, err = %d\n", __func__, err); goto out; } @@ -939,12 +955,9 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, struct ufs_pa_layer_attr *dev_max_params, struct ufs_pa_layer_attr *dev_req_params) { - u32 val; struct ufs_qcom_host *host = ufshcd_get_variant(hba); - struct phy *phy = host->generic_phy; struct ufs_qcom_dev_params ufs_qcom_cap; int ret = 0; - int res = 0; if (!dev_req_params) { pr_err("%s: incoming dev_req_params is NULL\n", __func__); @@ -1010,14 +1023,6 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, ret = -EINVAL; } - val = ~(MAX_U32 << dev_req_params->lane_tx); - res = ufs_qcom_phy_set_tx_lane_enable(phy, val); - if (res) { - dev_err(hba->dev, "%s: ufs_qcom_phy_set_tx_lane_enable() failed res = %d\n", - __func__, res); - ret = res; - } - /* cache the power mode parameters to use internally */ memcpy(&host->dev_req_params, dev_req_params, sizeof(*dev_req_params)); @@ -1087,12 +1092,12 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) { struct ufs_qcom_host *host = ufshcd_get_variant(hba); - if (host->hw_ver.major == 0x01) { + if (host->hw_ver.major == 0x1) { hba->quirks |= UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS | UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP | UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE; - if (host->hw_ver.minor == 0x0001 && host->hw_ver.step == 0x0001) + if (host->hw_ver.minor == 0x001 && host->hw_ver.step == 0x0001) hba->quirks |= UFSHCD_QUIRK_BROKEN_INTR_AGGR; hba->quirks |= UFSHCD_QUIRK_BROKEN_LCC; @@ -1147,7 +1152,8 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on, return 0; if (on && (status == POST_CHANGE)) { - phy_power_on(host->generic_phy); + phy_power_on(host->phy_lane0); + phy_power_on(host->phy_lane1); /* enable the device ref clock for HS mode*/ if (ufshcd_is_hs_mode(&hba->pwr_info)) @@ -1162,7 +1168,8 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on, ufs_qcom_dev_ref_clk_ctrl(host, false); /* powering off PHY during aggressive clk gating */ - phy_power_off(host->generic_phy); + phy_power_off(host->phy_lane0); + phy_power_off(host->phy_lane1); } vote = host->bus_vote.min_bw_vote; @@ -1225,21 +1232,46 @@ static int ufs_qcom_init(struct ufs_hba *hba) * skip devoting it during aggressive clock gating. This clock * will still be gated off during runtime suspend. */ - host->generic_phy = devm_phy_get(dev, "ufsphy"); + host->phy_lane0 = devm_phy_get(dev, "ufsphy_0"); + if (IS_ERR(host->phy_lane0)) { + err = PTR_ERR(host->phy_lane0); + if (err == -ENODEV) { + host->phy_lane0 = NULL; + } else if (err == -EPROBE_DEFER) { + /* + * UFS driver might be probed before the phy driver + * does. In that case we would like to return + * EPROBE_DEFER code. + */ + dev_warn(dev, "%s: required ufsphy_0 device. hasn't probed yet. err = %d\n", + __func__, err); + goto out_variant_clear; + } else { + dev_err(dev, "%s: ufsphy_0 get failed %d\n", + __func__, err); + goto out; + } + } - if (host->generic_phy == ERR_PTR(-EPROBE_DEFER)) { - /* - * UFS driver might be probed before the phy driver does. - * In that case we would like to return EPROBE_DEFER code. - */ - err = -EPROBE_DEFER; - dev_warn(dev, "%s: required phy device. hasn't probed yet. err = %d\n", - __func__, err); - goto out_variant_clear; - } else if (IS_ERR(host->generic_phy)) { - err = PTR_ERR(host->generic_phy); - dev_err(dev, "%s: PHY get failed %d\n", __func__, err); - goto out_variant_clear; + host->phy_lane1 = devm_phy_get(dev, "ufsphy_1"); + if (IS_ERR(host->phy_lane1)) { + err = PTR_ERR(host->phy_lane1); + if (err == -ENODEV) { + host->phy_lane1 = NULL; + } else if (err == -EPROBE_DEFER) { + /* + * UFS driver might be probed before the phy driver + * does. In that case we would like to return + * EPROBE_DEFER code. + */ + dev_warn(dev, "%s: required ufsphy_1 device. hasn't probed yet. err = %d\n", + __func__, err); + goto out_variant_clear; + } else { + dev_err(dev, "%s: ufsphy_1 get failed %d\n", + __func__, err); + goto out; + } } err = ufs_qcom_bus_register(host); @@ -1273,10 +1305,6 @@ static int ufs_qcom_init(struct ufs_hba *hba) } } - /* update phy revision information before calling phy_init() */ - ufs_qcom_phy_save_controller_version(host->generic_phy, - host->hw_ver.major, host->hw_ver.minor, host->hw_ver.step); - err = ufs_qcom_init_lane_clks(host); if (err) goto out_variant_clear; @@ -1311,8 +1339,10 @@ static void ufs_qcom_exit(struct ufs_hba *hba) struct ufs_qcom_host *host = ufshcd_get_variant(hba); ufs_qcom_disable_lane_clks(host); - phy_power_off(host->generic_phy); - phy_exit(host->generic_phy); + phy_power_off(host->phy_lane0); + phy_power_off(host->phy_lane1); + phy_exit(host->phy_lane0); + phy_exit(host->phy_lane1); } static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba, diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h index 295f4bef6a0e..06b744c8af1d 100644 --- a/drivers/scsi/ufs/ufs-qcom.h +++ b/drivers/scsi/ufs/ufs-qcom.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2015,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 @@ -223,7 +223,8 @@ struct ufs_qcom_host { #define UFS_QCOM_CAP_RETAIN_SEC_CFG_AFTER_PWR_COLLAPSE 0x2 u32 caps; - struct phy *generic_phy; + struct phy *phy_lane0; + struct phy *phy_lane1; struct ufs_hba *hba; struct ufs_qcom_bus_vote bus_vote; struct ufs_pa_layer_attr dev_req_params; diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index f51758f1e5cc..9f68b85538c1 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -3,7 +3,7 @@ * * This code is based on drivers/scsi/ufs/ufshcd.h * Copyright (C) 2011-2013 Samsung India Software Operations - * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. * * Authors: * Santosh Yaraganavi <santosh.sy@samsung.com> @@ -72,6 +72,8 @@ #define UFSHCD "ufshcd" #define UFSHCD_DRIVER_VERSION "0.2" +#define UFS_BIT(x) BIT(x) + struct ufs_hba; enum dev_cmd_type { @@ -865,6 +867,15 @@ static inline bool ufshcd_is_hs_mode(struct ufs_pa_layer_attr *pwr_info) pwr_info->pwr_tx == FASTAUTO_MODE); } +/* variant specific ops structures */ +#ifdef CONFIG_SCSI_UFS_MSM +extern const struct ufs_hba_variant_ops ufs_hba_msm_vops; +#else +static const struct ufs_hba_variant_ops ufs_hba_msm_vops = { + .name = "msm", +}; +#endif + /* Expose Query-Request API */ int ufshcd_query_descriptor_retry(struct ufs_hba *hba, enum query_opcode opcode, |