aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinaro CI <ci_notify@linaro.org>2018-06-17 10:13:07 +0000
committerLinaro CI <ci_notify@linaro.org>2018-06-17 10:13:07 +0000
commite6e232434425139e0cdbe8652f2fdcbefd87a3b7 (patch)
tree7370e44066d432ee1998382639a2b8602cdf64d3
parentbfeca2197f83971bb608eeaac637e1ccc11ed7ba (diff)
parent25a2232383d1a9bedc37342aa7ed54ebde4526d7 (diff)
Merge remote-tracking branch 'ufs/qcomlt/ufs' into integration-linux-qcomlt
-rw-r--r--Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt1
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp.c122
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp.h8
-rw-r--r--drivers/scsi/ufs/ufs-qcom.c172
-rw-r--r--drivers/scsi/ufs/ufs-qcom.h5
-rw-r--r--drivers/scsi/ufs/ufshcd.h13
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,