From 935b3e042d713f30ff766b3c27c86d8765c62357 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 29 Nov 2017 19:24:35 -0800 Subject: scsi: ufs: ufshcd: Enable no_write_same for scsi host Occasionally the following error message can be seen in the logs of Qualcomm devices using UFS: EXT4-fs (sda9): Delayed block allocation failed for inode 685600 at logical offset 1086 with max blocks 3 with error 121 EXT4-fs (sda9): This should not happen!! Data will be lost This is caused by a failing WRITE_SAME command, which per the JEDEC UFS specification is not a supported. Set the no_write_same flag on the ufshcd SCSI host to let the SCSI layer know this. Fixes: 7a3e97b0dc4b ("[SCSI] ufshcd: UFS Host controller driver") Cc: stable@vger.kernel.org Signed-off-by: Bjorn Andersson --- drivers/scsi/ufs/ufshcd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 397081d320b1..7bd5a086793b 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -6734,6 +6734,7 @@ static struct scsi_host_template ufshcd_driver_template = { .max_host_blocked = 1, .track_queue_depth = 1, .sdev_groups = ufshcd_driver_groups, + .no_write_same = 1, }; static int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg, -- cgit v1.2.3 From 5b4a2aee66dbf46d7aa114523b2ceb7233b4737f Mon Sep 17 00:00:00 2001 From: Can Guo Date: Mon, 18 Dec 2017 21:50:48 -0800 Subject: phy: qcom-qmp: add support for UFS PHY Add UFS PHY support to make UFS work with common PHY framework. Change-Id: Ic513927ff6f8c801dfed25573eddd493cb79a404 Signed-off-by: Can Guo Signed-off-by: Manu Gautam Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/phy/qcom-qmp-phy.txt | 1 + drivers/phy/qualcomm/phy-qcom-qmp.c | 123 ++++++++++++++++++++- drivers/phy/qualcomm/phy-qcom-qmp.h | 8 ++ 3 files changed, 126 insertions(+), 6 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..cb068bca4a13 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); @@ -1520,8 +1631,8 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { .compatible = "qcom,sdm845-qmp-usb3-phy", .data = &qmp_v3_usb3phy_cfg, }, { - .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 -- cgit v1.2.3 From 055080dd50187de92dbaf88d095100c8604971b1 Mon Sep 17 00:00:00 2001 From: Venkat Gopalakrishnan Date: Thu, 18 Aug 2016 16:58:20 -0700 Subject: scsi: ufs: add 2 lane support Qcom ufs controller v3.1.0 supports 2 lanes, add support to configure 2 lanes during phy initialization. Change-Id: Ifa2bac71b47ce49a6fb31ebb8dd54b243e1cfae1 Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Subhash Jadavani [cang@codeaurora.org: Resolved trivial merge conflicts] Signed-off-by: Can Guo Signed-off-by: Bjorn Andersson --- drivers/scsi/ufs/ufs-qcom.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 221820a7c78b..a6cdbafd6189 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -113,10 +113,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 +147,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 +186,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; -- cgit v1.2.3 From 641e3063d86d9f45a809f46745b70612cfe1fc4c Mon Sep 17 00:00:00 2001 From: Subhash Jadavani Date: Tue, 6 Jan 2015 17:16:13 -0800 Subject: scsi: ufs-qcom: add QUniPro hardware support New revisions of UFS host controller supports the new UniPro hardware controller (referred as QUniPro). This patch adds the support to enable this new UniPro controller hardware. Change-Id: Iccbcc38c36e6d9b9fcbb5c7fd7a3e3326c1c4ce0 Signed-off-by: Subhash Jadavani [subhashj@codeaurora.org: resolved trivial merge conflicts] Signed-off-by: Subhash Jadavani [cang@codeaurora.org: Resolved trivial merge conflicts] Signed-off-by: Can Guo Signed-off-by: Bjorn Andersson --- drivers/scsi/ufs/ufshcd.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index f51758f1e5cc..7478a36590fd 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -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 { -- cgit v1.2.3 From 0967163a42cd1b693183681f6958ee9b472b776a Mon Sep 17 00:00:00 2001 From: Subhash Jadavani Date: Fri, 19 Sep 2014 16:46:31 -0700 Subject: scsi: ufs-qcom: add support for new UFS controller revisions MSM8994v2 will have UFS controller revision 1.3.0 (major.minor.step) hence update the quirks check for this revision as well. Here is the list of UFS revisions (for quick reference): 8084 : 1.1.1 8994v1 : 1.2.0 8994v2 : 1.3.0 Future revisions: x.y.z where x >= 2 Change-Id: Iabc4b9117cda9d685e5a4fbaa9cc1f1bd40a5a60 Signed-off-by: Subhash Jadavani [cang@codeaurora.org: Resolved trivial merge conflicts] Signed-off-by: Can Guo Signed-off-by: Bjorn Andersson --- drivers/scsi/ufs/ufs-qcom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index a6cdbafd6189..e97cecd1f050 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -1085,12 +1085,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; -- cgit v1.2.3 From e6580eba78965a6e9f612db34171325753ae0eee Mon Sep 17 00:00:00 2001 From: Sujit Reddy Thumma Date: Mon, 8 Jul 2013 19:22:10 +0530 Subject: scsi: ufs: Add UFS host PHY driver for MSM targets Add MSM UFS host PHY driver which binds with UFSHCD platform driver exposing vendor specific operations to initialize the PHY. Since the controller and PHY are tightly coupled in the system, some of the MSM specific controller register configuration is also applied inline while initializing PHY. Add a new compatible property "qcom,ufshc" which specifies the UFS host controller on MSM platforms. The UFS controller driver binds with UFS PHY driver using the phandle reference of PHY devicetree node. Change-Id: If695a844d03268151c6c846bdfa6cee8ff84491b Signed-off-by: Sujit Reddy Thumma [subhashj@codeaurora.org: resolved trivial merge conflicts] Signed-off-by: Subhash Jadavani [venkatg@codeaurora.org: resolved trivial merge conflicts] Signed-off-by: Venkat Gopalakrishnan [cang@codeaurora.org: Resolved trivial merge conflicts, dropped all changes from ufs-msm.c] Signed-off-by: Can Guo Signed-off-by: Bjorn Andersson --- drivers/scsi/ufs/ufshcd.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 7478a36590fd..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 @@ -867,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, -- cgit v1.2.3 From 0732a58177f3e97f3cc1fd73f28c3829b27f066b Mon Sep 17 00:00:00 2001 From: Can Guo Date: Mon, 18 Dec 2017 22:31:56 -0800 Subject: wscsi: ufs-qcom: Operate each lane separately. To make UFS work with common PHY driver, UFSHCD controller should operate each lane as a generic PHY serparately. Change-Id: I98da368690df7d441c3af709f619908c9c0cb8a3 Signed-off-by: Can Guo Signed-off-by: Bjorn Andersson --- drivers/scsi/ufs/ufs-qcom.c | 148 +++++++++++++++++++++++++++----------------- drivers/scsi/ufs/ufs-qcom.h | 5 +- 2 files changed, 93 insertions(+), 60 deletions(-) diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index e97cecd1f050..60ad6f5bec86 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -16,7 +16,6 @@ #include #include #include -#include #include "ufshcd.h" #include "ufshcd-pltfrm.h" @@ -196,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; } @@ -266,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); @@ -280,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 lane0 init failed, ret = %d\n", + __func__, ret); + goto out; + } + + ret = phy_init(phy_lane1); if (ret) { - dev_err(hba->dev, "%s: phy init failed, ret = %d\n", + dev_err(hba->dev, "%s: phy lane1 init failed, ret = %d\n", __func__, ret); goto out; } @@ -297,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; } @@ -310,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; } @@ -561,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)) { @@ -571,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); @@ -584,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: @@ -594,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 regs, err = %d\n", + 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 phy lane1 regs, err = %d\n", __func__, err); goto out; } @@ -937,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__); @@ -1008,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)); @@ -1145,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)) @@ -1160,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; @@ -1223,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); @@ -1271,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; @@ -1309,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; -- cgit v1.2.3