Hello,
This series adds HS-G4 support to the Qcom UFS driver and PHY driver.
The newer Qcom platforms support configuring the UFS controller and PHY
in dual gears (i.e., controller/PHY can be configured to run in two gear
speeds). This is accomplished by adding two different PHY init sequences
to the PHY driver and the UFS driver requesting the one that's required
based on the platform configuration.
But this requires both the UFS controller and UFS device to agree to a
common gear. For finding the max supported gear, a separate register is
used for the UFS controller and devicetree is used for the UFS device.
Based on the max gear of both, the UFS driver will decide which gear to
use during runtime.
This series has been tested on Qcom RB5 development platform powered by
SM8250 SoC that uses HS-G4.
Merging Strategy:
-----------------
The PHY patches are expected to go through PHY tree and UFS, MAINTAINERS
patches are expected to go through SCSI tree. Finally, the binding and
devicetree patches can go through ARM MSM tree. There is no build dependency
between the patches.
Thanks,
Mani
Changes in v2:
* Collected reviews from Dmitry
* Renamed "max-gear" property to "max-device-gear"
* Used min() for deciding which gear to use instead of open comparision
* Added comment about the old register name
Manivannan Sadhasivam (15):
phy: qcom-qmp-ufs: Move register settings to qmp_phy_cfg_tables struct
phy: qcom-qmp-ufs: Add support for configuring PHY in HS Series B mode
phy: qcom-qmp-ufs: Add support for configuring PHY in HS G4 mode
phy: qcom-qmp-ufs: Add HS G4 mode support to SM8250 SoC
phy: qcom-qmp-ufs: Move HS Rate B register setting to tables_hs_b
dt-bindings: ufs: Add "max-device-gear" property for UFS device
arm64: dts: qcom: qrb5165-rb5: Add max-device-gear property to UFS
node
scsi: ufs: ufs-qcom: Remove un-necessary goto statements
scsi: ufs: ufs-qcom: Remove un-necessary WARN_ON()
scsi: ufs: ufs-qcom: Use bitfields where appropriate
scsi: ufs: ufs-qcom: Use dev_err_probe() for printing probe error
scsi: ufs: ufs-qcom: Fix the Qcom register name for offset 0xD0
scsi: ufs: ufs-qcom: Factor out the logic finding the HS Gear
scsi: ufs: ufs-qcom: Add support for finding HS gear on new UFS
versions
MAINTAINERS: Add myself as the maintainer for Qcom UFS driver
.../devicetree/bindings/ufs/ufs-common.yaml | 5 +
MAINTAINERS | 8 +
arch/arm64/boot/dts/qcom/qrb5165-rb5.dts | 1 +
drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 350 +++++++++++++-----
drivers/ufs/host/ufs-qcom.c | 175 +++++----
drivers/ufs/host/ufs-qcom.h | 68 ++--
6 files changed, 390 insertions(+), 217 deletions(-)
--
2.25.1
Add "max-device-gear" property to UFS node to specify the maximum gear
speed supported by the UFS device on the RB5 board.
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
arch/arm64/boot/dts/qcom/qrb5165-rb5.dts | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
index bf8077a1cf9a..3cb1f48c90f5 100644
--- a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
+++ b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
@@ -1250,6 +1250,7 @@ &uart12 {
&ufs_mem_hc {
status = "okay";
+ max-device-gear = <4>;
vcc-supply = <&vreg_l17a_3p0>;
vcc-max-microamp = <800000>;
vccq-supply = <&vreg_l6a_1p2>;
--
2.25.1
goto in error path is useful if the function needs to do cleanup other
than returning the error code. But in this driver, goto statements are
used for just returning the error code in many places. This really
makes it hard to read the code.
So let's get rid of those goto statements and just return the error code
directly.
Reviewed-by: Dmitry Baryshkov <[email protected]>
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/ufs/host/ufs-qcom.c | 100 +++++++++++++++---------------------
1 file changed, 41 insertions(+), 59 deletions(-)
diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
index 8ad1415e10b6..7cd996ac180b 100644
--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -110,7 +110,7 @@ static void ufs_qcom_disable_lane_clks(struct ufs_qcom_host *host)
static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host *host)
{
- int err = 0;
+ int err;
struct device *dev = host->hba->dev;
if (host->is_lane_clks_enabled)
@@ -119,7 +119,7 @@ static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host *host)
err = ufs_qcom_host_clk_enable(dev, "rx_lane0_sync_clk",
host->rx_l0_sync_clk);
if (err)
- goto out;
+ return err;
err = ufs_qcom_host_clk_enable(dev, "tx_lane0_sync_clk",
host->tx_l0_sync_clk);
@@ -137,7 +137,8 @@ static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host *host)
goto disable_rx_l1;
host->is_lane_clks_enabled = true;
- goto out;
+
+ return 0;
disable_rx_l1:
clk_disable_unprepare(host->rx_l1_sync_clk);
@@ -145,7 +146,7 @@ static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host *host)
clk_disable_unprepare(host->tx_l0_sync_clk);
disable_rx_l0:
clk_disable_unprepare(host->rx_l0_sync_clk);
-out:
+
return err;
}
@@ -160,25 +161,25 @@ static int ufs_qcom_init_lane_clks(struct ufs_qcom_host *host)
err = ufs_qcom_host_clk_get(dev, "rx_lane0_sync_clk",
&host->rx_l0_sync_clk, false);
if (err)
- goto out;
+ return err;
err = ufs_qcom_host_clk_get(dev, "tx_lane0_sync_clk",
&host->tx_l0_sync_clk, false);
if (err)
- goto out;
+ return err;
/* In case of single lane per direction, don't read lane1 clocks */
if (host->hba->lanes_per_direction > 1) {
err = ufs_qcom_host_clk_get(dev, "rx_lane1_sync_clk",
&host->rx_l1_sync_clk, false);
if (err)
- goto out;
+ return err;
err = ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk",
&host->tx_l1_sync_clk, true);
}
-out:
- return err;
+
+ return 0;
}
static int ufs_qcom_check_hibern8(struct ufs_hba *hba)
@@ -241,7 +242,7 @@ static int ufs_qcom_host_reset(struct ufs_hba *hba)
if (!host->core_reset) {
dev_warn(hba->dev, "%s: reset control not set\n", __func__);
- goto out;
+ return 0;
}
reenable_intr = hba->is_irq_enabled;
@@ -252,7 +253,7 @@ static int ufs_qcom_host_reset(struct ufs_hba *hba)
if (ret) {
dev_err(hba->dev, "%s: core_reset assert failed, err = %d\n",
__func__, ret);
- goto out;
+ return ret;
}
/*
@@ -274,15 +275,14 @@ static int ufs_qcom_host_reset(struct ufs_hba *hba)
hba->is_irq_enabled = true;
}
-out:
- return ret;
+ return 0;
}
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;
- int ret = 0;
+ int ret;
bool is_rate_B = UFS_QCOM_LIMIT_HS_RATE == PA_HS_MODE_B;
/* Reset UFS Host Controller and PHY */
@@ -299,7 +299,7 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
if (ret) {
dev_err(hba->dev, "%s: phy init failed, ret = %d\n",
__func__, ret);
- goto out;
+ return ret;
}
/* power on phy - start serdes and phy's power and clocks */
@@ -316,7 +316,7 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
out_disable_phy:
phy_exit(phy);
-out:
+
return ret;
}
@@ -374,7 +374,6 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba,
static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear,
u32 hs, u32 rate, bool update_link_startup_timer)
{
- int ret = 0;
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
struct ufs_clk_info *clki;
u32 core_clk_period_in_ns;
@@ -409,11 +408,11 @@ static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear,
* Aggregation logic.
*/
if (ufs_qcom_cap_qunipro(host) && !ufshcd_is_intr_aggr_allowed(hba))
- goto out;
+ return 0;
if (gear == 0) {
dev_err(hba->dev, "%s: invalid gear = %d\n", __func__, gear);
- goto out_error;
+ return -EINVAL;
}
list_for_each_entry(clki, &hba->clk_list_head, list) {
@@ -436,7 +435,7 @@ static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear,
}
if (ufs_qcom_cap_qunipro(host))
- goto out;
+ return 0;
core_clk_period_in_ns = NSEC_PER_SEC / core_clk_rate;
core_clk_period_in_ns <<= OFFSET_CLK_NS_REG;
@@ -451,7 +450,7 @@ static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear,
"%s: index %d exceeds table size %zu\n",
__func__, gear,
ARRAY_SIZE(hs_fr_table_rA));
- goto out_error;
+ return -EINVAL;
}
tx_clk_cycles_per_us = hs_fr_table_rA[gear-1][1];
} else if (rate == PA_HS_MODE_B) {
@@ -460,13 +459,13 @@ static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear,
"%s: index %d exceeds table size %zu\n",
__func__, gear,
ARRAY_SIZE(hs_fr_table_rB));
- goto out_error;
+ return -EINVAL;
}
tx_clk_cycles_per_us = hs_fr_table_rB[gear-1][1];
} else {
dev_err(hba->dev, "%s: invalid rate = %d\n",
__func__, rate);
- goto out_error;
+ return -EINVAL;
}
break;
case SLOWAUTO_MODE:
@@ -476,14 +475,14 @@ static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear,
"%s: index %d exceeds table size %zu\n",
__func__, gear,
ARRAY_SIZE(pwm_fr_table));
- goto out_error;
+ return -EINVAL;
}
tx_clk_cycles_per_us = pwm_fr_table[gear-1][1];
break;
case UNCHANGED:
default:
dev_err(hba->dev, "%s: invalid mode = %d\n", __func__, hs);
- goto out_error;
+ return -EINVAL;
}
if (ufshcd_readl(hba, REG_UFS_TX_SYMBOL_CLK_NS_US) !=
@@ -507,12 +506,8 @@ static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear,
*/
mb();
}
- goto out;
-out_error:
- ret = -EINVAL;
-out:
- return ret;
+ return 0;
}
static int ufs_qcom_link_startup_notify(struct ufs_hba *hba,
@@ -527,8 +522,7 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba *hba,
0, true)) {
dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n",
__func__);
- err = -EINVAL;
- goto out;
+ return -EINVAL;
}
if (ufs_qcom_cap_qunipro(host))
@@ -554,7 +548,6 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba *hba,
break;
}
-out:
return err;
}
@@ -691,8 +684,7 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
if (!dev_req_params) {
pr_err("%s: incoming dev_req_params is NULL\n", __func__);
- ret = -EINVAL;
- goto out;
+ return -EINVAL;
}
switch (status) {
@@ -720,7 +712,7 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
if (ret) {
pr_err("%s: failed to determine capabilities\n",
__func__);
- goto out;
+ return ret;
}
/* enable the device ref clock before changing to HS mode */
@@ -761,7 +753,7 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
ret = -EINVAL;
break;
}
-out:
+
return ret;
}
@@ -773,14 +765,11 @@ static int ufs_qcom_quirk_host_pa_saveconfigtime(struct ufs_hba *hba)
err = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_VS_CONFIG_REG1),
&pa_vs_config_reg1);
if (err)
- goto out;
+ return err;
/* Allow extension of MSB bits of PA_SaveConfigTime attribute */
- err = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_VS_CONFIG_REG1),
+ return ufshcd_dme_set(hba, UIC_ARG_MIB(PA_VS_CONFIG_REG1),
(pa_vs_config_reg1 | (1 << 12)));
-
-out:
- return err;
}
static int ufs_qcom_apply_dev_quirks(struct ufs_hba *hba)
@@ -957,9 +946,8 @@ static int ufs_qcom_init(struct ufs_hba *hba)
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
if (!host) {
- err = -ENOMEM;
dev_err(dev, "%s: no memory for qcom ufs host\n", __func__);
- goto out;
+ return -ENOMEM;
}
/* Make a two way bind between the qcom host and the hba */
@@ -980,10 +968,8 @@ static int ufs_qcom_init(struct ufs_hba *hba)
host->rcdev.owner = dev->driver->owner;
host->rcdev.nr_resets = 1;
err = devm_reset_controller_register(dev, &host->rcdev);
- if (err) {
+ if (err)
dev_warn(dev, "Failed to register reset controller\n");
- err = 0;
- }
if (!has_acpi_companion(dev)) {
host->generic_phy = devm_phy_get(dev, "ufsphy");
@@ -1049,17 +1035,16 @@ static int ufs_qcom_init(struct ufs_hba *hba)
host->dbg_print_en |= UFS_QCOM_DEFAULT_DBG_PRINT_EN;
ufs_qcom_get_default_testbus_cfg(host);
err = ufs_qcom_testbus_config(host);
- if (err) {
+ if (err)
+ /* Failure is non-fatal */
dev_warn(dev, "%s: failed to configure the testbus %d\n",
__func__, err);
- err = 0;
- }
- goto out;
+ return 0;
out_variant_clear:
ufshcd_set_variant(hba, NULL);
-out:
+
return err;
}
@@ -1085,7 +1070,7 @@ static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba,
UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL),
&core_clk_ctrl_reg);
if (err)
- goto out;
+ return err;
core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK;
core_clk_ctrl_reg |= clk_cycles;
@@ -1093,11 +1078,9 @@ static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba,
/* Clear CORE_CLK_DIV_EN */
core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT;
- err = ufshcd_dme_set(hba,
+ return ufshcd_dme_set(hba,
UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL),
core_clk_ctrl_reg);
-out:
- return err;
}
static int ufs_qcom_clk_scale_up_pre_change(struct ufs_hba *hba)
@@ -1180,7 +1163,7 @@ static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba,
if (err || !dev_req_params) {
ufshcd_uic_hibern8_exit(hba);
- goto out;
+ return err;
}
ufs_qcom_cfg_timers(hba,
@@ -1191,8 +1174,7 @@ static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba,
ufshcd_uic_hibern8_exit(hba);
}
-out:
- return err;
+ return 0;
}
static void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba,
--
2.25.1
Qcom UFS driver has been left un-maintained till now. I'd like to step
up to maintain the driver and its binding.
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
MAINTAINERS | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index cf0f18502372..149fd6daf52b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21097,6 +21097,14 @@ L: [email protected] (moderated for non-subscribers)
S: Maintained
F: drivers/ufs/host/ufs-mediatek*
+UNIVERSAL FLASH STORAGE HOST CONTROLLER DRIVER QUALCOMM HOOKS
+M: Manivannan Sadhasivam <[email protected]>
+L: [email protected]
+L: [email protected]
+S: Maintained
+F: Documentation/devicetree/bindings/ufs/qcom,ufs.yaml
+F: drivers/ufs/host/ufs-qcom.c
+
UNIVERSAL FLASH STORAGE HOST CONTROLLER DRIVER RENESAS HOOKS
M: Yoshihiro Shimoda <[email protected]>
L: [email protected]
--
2.25.1
The maximum gear supported by the UFS device can be specified using the
"max-device-gear" property. This allows the UFS controller to configure the
TX/RX gear before starting communication with the UFS device.
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
Documentation/devicetree/bindings/ufs/ufs-common.yaml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/Documentation/devicetree/bindings/ufs/ufs-common.yaml b/Documentation/devicetree/bindings/ufs/ufs-common.yaml
index 47a4e9e1a775..5dcd14909ad5 100644
--- a/Documentation/devicetree/bindings/ufs/ufs-common.yaml
+++ b/Documentation/devicetree/bindings/ufs/ufs-common.yaml
@@ -73,6 +73,11 @@ properties:
description:
Specifies max. load that can be drawn from VCCQ2 supply.
+ max-device-gear:
+ description:
+ Specifies max. gear the UFS device supports.
+ enum: [1, 2, 3, 4, 5]
+
dependencies:
freq-table-hz: [ 'clocks' ]
--
2.25.1
Use bitfield macros where appropriate to simplify the driver.
Reviewed-by: Dmitry Baryshkov <[email protected]>
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/ufs/host/ufs-qcom.h | 61 +++++++++++++++++--------------------
1 file changed, 28 insertions(+), 33 deletions(-)
diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h
index 44466a395bb5..9d96ac71b27f 100644
--- a/drivers/ufs/host/ufs-qcom.h
+++ b/drivers/ufs/host/ufs-qcom.h
@@ -17,12 +17,9 @@
#define DEFAULT_CLK_RATE_HZ 1000000
#define BUS_VECTOR_NAME_LEN 32
-#define UFS_HW_VER_MAJOR_SHFT (28)
-#define UFS_HW_VER_MAJOR_MASK (0x000F << UFS_HW_VER_MAJOR_SHFT)
-#define UFS_HW_VER_MINOR_SHFT (16)
-#define UFS_HW_VER_MINOR_MASK (0x0FFF << UFS_HW_VER_MINOR_SHFT)
-#define UFS_HW_VER_STEP_SHFT (0)
-#define UFS_HW_VER_STEP_MASK (0xFFFF << UFS_HW_VER_STEP_SHFT)
+#define UFS_HW_VER_MAJOR_MASK GENMASK(31, 28)
+#define UFS_HW_VER_MINOR_MASK GENMASK(27, 16)
+#define UFS_HW_VER_STEP_MASK GENMASK(15, 0)
/* vendor specific pre-defined parameters */
#define SLOW 1
@@ -76,24 +73,28 @@ enum {
#define UFS_CNTLR_3_x_x_VEN_REGS_OFFSET(x) (0x400 + x)
/* bit definitions for REG_UFS_CFG1 register */
-#define QUNIPRO_SEL 0x1
-#define UTP_DBG_RAMS_EN 0x20000
+#define QUNIPRO_SEL BIT(0)
+#define UFS_PHY_SOFT_RESET BIT(1)
+#define UTP_DBG_RAMS_EN BIT(17)
#define TEST_BUS_EN BIT(18)
#define TEST_BUS_SEL GENMASK(22, 19)
#define UFS_REG_TEST_BUS_EN BIT(30)
+#define UFS_PHY_RESET_ENABLE 1
+#define UFS_PHY_RESET_DISABLE 0
+
/* bit definitions for REG_UFS_CFG2 register */
-#define UAWM_HW_CGC_EN (1 << 0)
-#define UARM_HW_CGC_EN (1 << 1)
-#define TXUC_HW_CGC_EN (1 << 2)
-#define RXUC_HW_CGC_EN (1 << 3)
-#define DFC_HW_CGC_EN (1 << 4)
-#define TRLUT_HW_CGC_EN (1 << 5)
-#define TMRLUT_HW_CGC_EN (1 << 6)
-#define OCSC_HW_CGC_EN (1 << 7)
+#define UAWM_HW_CGC_EN BIT(0)
+#define UARM_HW_CGC_EN BIT(1)
+#define TXUC_HW_CGC_EN BIT(2)
+#define RXUC_HW_CGC_EN BIT(3)
+#define DFC_HW_CGC_EN BIT(4)
+#define TRLUT_HW_CGC_EN BIT(5)
+#define TMRLUT_HW_CGC_EN BIT(6)
+#define OCSC_HW_CGC_EN BIT(7)
/* bit definition for UFS_UFS_TEST_BUS_CTRL_n */
-#define TEST_BUS_SUB_SEL_MASK 0x1F /* All XXX_SEL fields are 5 bits wide */
+#define TEST_BUS_SUB_SEL_MASK GENMASK(4, 0) /* All XXX_SEL fields are 5 bits wide */
#define REG_UFS_CFG2_CGC_EN_ALL (UAWM_HW_CGC_EN | UARM_HW_CGC_EN |\
TXUC_HW_CGC_EN | RXUC_HW_CGC_EN |\
@@ -101,17 +102,11 @@ enum {
TMRLUT_HW_CGC_EN | OCSC_HW_CGC_EN)
/* bit offset */
-enum {
- OFFSET_UFS_PHY_SOFT_RESET = 1,
- OFFSET_CLK_NS_REG = 10,
-};
+#define OFFSET_CLK_NS_REG 0xa
/* bit masks */
-enum {
- MASK_UFS_PHY_SOFT_RESET = 0x2,
- MASK_TX_SYMBOL_CLK_1US_REG = 0x3FF,
- MASK_CLK_NS_REG = 0xFFFC00,
-};
+#define MASK_TX_SYMBOL_CLK_1US_REG GENMASK(9, 0)
+#define MASK_CLK_NS_REG GENMASK(23, 10)
/* QCOM UFS debug print bit mask */
#define UFS_QCOM_DBG_PRINT_REGS_EN BIT(0)
@@ -135,15 +130,15 @@ ufs_qcom_get_controller_revision(struct ufs_hba *hba,
{
u32 ver = ufshcd_readl(hba, REG_UFS_HW_VERSION);
- *major = (ver & UFS_HW_VER_MAJOR_MASK) >> UFS_HW_VER_MAJOR_SHFT;
- *minor = (ver & UFS_HW_VER_MINOR_MASK) >> UFS_HW_VER_MINOR_SHFT;
- *step = (ver & UFS_HW_VER_STEP_MASK) >> UFS_HW_VER_STEP_SHFT;
+ *major = FIELD_GET(UFS_HW_VER_MAJOR_MASK, ver);
+ *minor = FIELD_GET(UFS_HW_VER_MINOR_MASK, ver);
+ *step = FIELD_GET(UFS_HW_VER_STEP_MASK, ver);
};
static inline void ufs_qcom_assert_reset(struct ufs_hba *hba)
{
- ufshcd_rmwl(hba, MASK_UFS_PHY_SOFT_RESET,
- 1 << OFFSET_UFS_PHY_SOFT_RESET, REG_UFS_CFG1);
+ ufshcd_rmwl(hba, UFS_PHY_SOFT_RESET, FIELD_PREP(UFS_PHY_SOFT_RESET, UFS_PHY_RESET_ENABLE),
+ REG_UFS_CFG1);
/*
* Make sure assertion of ufs phy reset is written to
@@ -154,8 +149,8 @@ static inline void ufs_qcom_assert_reset(struct ufs_hba *hba)
static inline void ufs_qcom_deassert_reset(struct ufs_hba *hba)
{
- ufshcd_rmwl(hba, MASK_UFS_PHY_SOFT_RESET,
- 0 << OFFSET_UFS_PHY_SOFT_RESET, REG_UFS_CFG1);
+ ufshcd_rmwl(hba, UFS_PHY_SOFT_RESET, FIELD_PREP(UFS_PHY_SOFT_RESET, UFS_PHY_RESET_DISABLE),
+ REG_UFS_CFG1);
/*
* Make sure de-assertion of ufs phy reset is written to
--
2.25.1
Add separate tables_hs_g4 instance to allow the PHY driver to configure the
PHY in HS G4 mode. The individual SoC configs need to supply the Rx, Tx and
PCS register setting in tables_hs_g4 and the UFS driver can request the
Hs G4 mode by calling phy_set_mode_ext() with submode set to UFS_HS_G4.
Reviewed-by: Dmitry Baryshkov <[email protected]>
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
index 4c6a2b5afc9a..5f2a012707b7 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
@@ -553,6 +553,8 @@ struct qmp_phy_cfg {
const struct qmp_phy_cfg_tables tables;
/* Additional sequence for HS Series B */
const struct qmp_phy_cfg_tables tables_hs_b;
+ /* Additional sequence for HS G4 */
+ const struct qmp_phy_cfg_tables tables_hs_g4;
/* clock ids to be requested */
const char * const *clk_list;
@@ -587,6 +589,7 @@ struct qmp_phy_cfg {
* @pcs_misc: iomapped memory space for lane's pcs_misc
* @qmp: QMP phy to which this lane belongs
* @mode: PHY mode configured by the UFS driver
+ * @submode: PHY submode configured by the UFS driver
*/
struct qmp_phy {
struct phy *phy;
@@ -600,6 +603,7 @@ struct qmp_phy {
void __iomem *pcs_misc;
struct qcom_qmp *qmp;
u32 mode;
+ u32 submode;
};
/**
@@ -993,8 +997,12 @@ static int qmp_ufs_power_on(struct phy *phy)
qmp_ufs_serdes_init(qphy, &cfg->tables_hs_b);
qmp_ufs_lanes_init(qphy, &cfg->tables);
+ if (qphy->submode == UFS_HS_G4)
+ qmp_ufs_lanes_init(qphy, &cfg->tables_hs_g4);
qmp_ufs_pcs_init(qphy, &cfg->tables);
+ if (qphy->submode == UFS_HS_G4)
+ qmp_ufs_pcs_init(qphy, &cfg->tables_hs_g4);
ret = reset_control_deassert(qmp->ufs_reset);
if (ret)
@@ -1083,6 +1091,7 @@ static int qmp_ufs_set_mode(struct phy *phy, enum phy_mode mode, int submode)
struct qmp_phy *qphy = phy_get_drvdata(phy);
qphy->mode = mode;
+ qphy->submode = submode;
return 0;
}
--
2.25.1
In the preparation of adding support for new gears, let's move the
logic that finds the gear for each platform to a new function. This helps
with code readability and also allows the logic to be used in other places
of the driver in future.
While at it, let's make it clear that this driver only supports symmetric
gear setting (hs_tx_gear == hs_rx_gear).
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/ufs/host/ufs-qcom.c | 36 +++++++++++++++++++++++-------------
1 file changed, 23 insertions(+), 13 deletions(-)
diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
index 38e2ed749d75..c93d2d38b43e 100644
--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -278,6 +278,26 @@ static int ufs_qcom_host_reset(struct ufs_hba *hba)
return 0;
}
+static u32 ufs_qcom_get_hs_gear(struct ufs_hba *hba, u32 hs_gear)
+{
+ struct ufs_qcom_host *host = ufshcd_get_variant(hba);
+
+ if (host->hw_ver.major == 0x1) {
+ /*
+ * HS-G3 operations may not reliably work on legacy QCOM
+ * UFS host controller hardware even though capability
+ * exchange during link startup phase may end up
+ * negotiating maximum supported gear as G3.
+ * Hence downgrade the maximum supported gear to HS-G2.
+ */
+ if (hs_gear > UFS_HS_G2)
+ return UFS_HS_G2;
+ }
+
+ /* Default is HS-G3 */
+ return UFS_HS_G3;
+}
+
static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
{
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
@@ -692,19 +712,9 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
ufshcd_init_pwr_dev_param(&ufs_qcom_cap);
ufs_qcom_cap.hs_rate = UFS_QCOM_LIMIT_HS_RATE;
- if (host->hw_ver.major == 0x1) {
- /*
- * HS-G3 operations may not reliably work on legacy QCOM
- * UFS host controller hardware even though capability
- * exchange during link startup phase may end up
- * negotiating maximum supported gear as G3.
- * Hence downgrade the maximum supported gear to HS-G2.
- */
- if (ufs_qcom_cap.hs_tx_gear > UFS_HS_G2)
- ufs_qcom_cap.hs_tx_gear = UFS_HS_G2;
- if (ufs_qcom_cap.hs_rx_gear > UFS_HS_G2)
- ufs_qcom_cap.hs_rx_gear = UFS_HS_G2;
- }
+ /* This driver only supports symmetic gear setting i.e., hs_tx_gear == hs_rx_gear */
+ ufs_qcom_cap.hs_tx_gear = ufs_qcom_cap.hs_rx_gear = ufs_qcom_get_hs_gear(hba,
+ ufs_qcom_cap.hs_tx_gear);
ret = ufshcd_get_pwr_dev_param(&ufs_qcom_cap,
dev_max_params,
--
2.25.1
Since now there is support for configuring the HS Rate B mode properly,
let's move the register setting to tables_hs_b struct for all SoCs.
This allows the PHY to be configured in Rate A initially and then in
Rate B if requested by the UFS driver.
Reviewed-by: Dmitry Baryshkov <[email protected]>
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 32 +++++++++++++++++++++----
1 file changed, 28 insertions(+), 4 deletions(-)
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
index fa7457c0202b..c55c85a8f95e 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
@@ -228,8 +228,9 @@ static const struct qmp_phy_init_tbl sm6115_ufsphy_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_INITVAL1, 0xff),
QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_INITVAL2, 0x00),
+};
- /* Rate B */
+static const struct qmp_phy_init_tbl sm6115_ufsphy_hs_b_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x44),
};
@@ -305,8 +306,9 @@ static const struct qmp_phy_init_tbl sdm845_ufsphy_serdes[] = {
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 */
+static const struct qmp_phy_init_tbl sdm845_ufsphy_hs_b_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x44),
};
@@ -371,8 +373,9 @@ static const struct qmp_phy_init_tbl sm8150_ufsphy_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xdd),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x23),
+};
- /* Rate B */
+static const struct qmp_phy_init_tbl sm8150_ufsphy_hs_b_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x06),
};
@@ -492,8 +495,9 @@ static const struct qmp_phy_init_tbl sm8350_ufsphy_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE1, 0x1e),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xdd),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x23),
+};
- /* Rate B */
+static const struct qmp_phy_init_tbl sm8350_ufsphy_hs_b_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAP, 0x06),
};
@@ -746,6 +750,10 @@ static const struct qmp_phy_cfg sdm845_ufsphy_cfg = {
.pcs = sdm845_ufsphy_pcs,
.pcs_num = ARRAY_SIZE(sdm845_ufsphy_pcs),
},
+ .tables_hs_b = {
+ .serdes = sdm845_ufsphy_hs_b_serdes,
+ .serdes_num = ARRAY_SIZE(sdm845_ufsphy_hs_b_serdes),
+ },
.clk_list = sdm845_ufs_phy_clk_l,
.num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
.vreg_list = qmp_phy_vreg_l,
@@ -772,6 +780,10 @@ static const struct qmp_phy_cfg sm6115_ufsphy_cfg = {
.pcs = sm6115_ufsphy_pcs,
.pcs_num = ARRAY_SIZE(sm6115_ufsphy_pcs),
},
+ .tables_hs_b = {
+ .serdes = sm6115_ufsphy_hs_b_serdes,
+ .serdes_num = ARRAY_SIZE(sm6115_ufsphy_hs_b_serdes),
+ },
.clk_list = sdm845_ufs_phy_clk_l,
.num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
.vreg_list = qmp_phy_vreg_l,
@@ -797,6 +809,10 @@ static const struct qmp_phy_cfg sm8150_ufsphy_cfg = {
.pcs = sm8150_ufsphy_pcs,
.pcs_num = ARRAY_SIZE(sm8150_ufsphy_pcs),
},
+ .tables_hs_b = {
+ .serdes = sm8150_ufsphy_hs_b_serdes,
+ .serdes_num = ARRAY_SIZE(sm8150_ufsphy_hs_b_serdes),
+ },
.clk_list = sdm845_ufs_phy_clk_l,
.num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
.vreg_list = qmp_phy_vreg_l,
@@ -829,6 +845,10 @@ static const struct qmp_phy_cfg sm8250_ufsphy_cfg = {
.pcs = sm8250_ufsphy_hs_g4_pcs,
.pcs_num = ARRAY_SIZE(sm8250_ufsphy_hs_g4_pcs),
},
+ .tables_hs_b = {
+ .serdes = sm8150_ufsphy_hs_b_serdes,
+ .serdes_num = ARRAY_SIZE(sm8150_ufsphy_hs_b_serdes),
+ },
.clk_list = sdm845_ufs_phy_clk_l,
.num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
.vreg_list = qmp_phy_vreg_l,
@@ -853,6 +873,10 @@ static const struct qmp_phy_cfg sm8350_ufsphy_cfg = {
.pcs = sm8350_ufsphy_pcs,
.pcs_num = ARRAY_SIZE(sm8350_ufsphy_pcs),
},
+ .tables_hs_b = {
+ .serdes = sm8350_ufsphy_hs_b_serdes,
+ .serdes_num = ARRAY_SIZE(sm8350_ufsphy_hs_b_serdes),
+ },
.clk_list = sdm845_ufs_phy_clk_l,
.num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
.vreg_list = qmp_phy_vreg_l,
--
2.25.1
As done for Qcom PCIe PHY driver, let's move the register settings to the
common qmp_phy_cfg_tables struct. This helps in adding any additional PHY
settings needed for functionalities like HS-G4 in the future by adding one
more instance of the qmp_phy_cfg_tables.
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 223 +++++++++++++-----------
1 file changed, 126 insertions(+), 97 deletions(-)
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
index c08d34ad1313..cdfda4e6d575 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
@@ -107,7 +107,7 @@ static const unsigned int sm8150_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = {
[QPHY_SW_RESET] = QPHY_V4_PCS_UFS_SW_RESET,
};
-static const struct qmp_phy_init_tbl msm8996_ufs_serdes_tbl[] = {
+static const struct qmp_phy_init_tbl msm8996_ufs_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x0e),
QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0xd7),
QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
@@ -156,12 +156,12 @@ static const struct qmp_phy_init_tbl msm8996_ufs_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE1, 0x00),
};
-static const struct qmp_phy_init_tbl msm8996_ufs_tx_tbl[] = {
+static const struct qmp_phy_init_tbl msm8996_ufs_tx[] = {
QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x02),
};
-static const struct qmp_phy_init_tbl msm8996_ufs_rx_tbl[] = {
+static const struct qmp_phy_init_tbl msm8996_ufs_rx[] = {
QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x24),
QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x02),
QMP_PHY_INIT_CFG(QSERDES_RX_RX_INTERFACE_MODE, 0x00),
@@ -175,7 +175,7 @@ static const struct qmp_phy_init_tbl msm8996_ufs_rx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E),
};
-static const struct qmp_phy_init_tbl sm6115_ufsphy_serdes_tbl[] = {
+static const struct qmp_phy_init_tbl sm6115_ufsphy_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x0e),
QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14),
QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
@@ -231,12 +231,12 @@ static const struct qmp_phy_init_tbl sm6115_ufsphy_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x44),
};
-static const struct qmp_phy_init_tbl sm6115_ufsphy_tx_tbl[] = {
+static const struct qmp_phy_init_tbl sm6115_ufsphy_tx[] = {
QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
};
-static const struct qmp_phy_init_tbl sm6115_ufsphy_rx_tbl[] = {
+static const struct qmp_phy_init_tbl sm6115_ufsphy_rx[] = {
QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x24),
QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x0F),
QMP_PHY_INIT_CFG(QSERDES_RX_RX_INTERFACE_MODE, 0x40),
@@ -254,7 +254,7 @@ static const struct qmp_phy_init_tbl sm6115_ufsphy_rx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x5B),
};
-static const struct qmp_phy_init_tbl sm6115_ufsphy_pcs_tbl[] = {
+static const struct qmp_phy_init_tbl sm6115_ufsphy_pcs[] = {
QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_PWM_GEAR_BAND, 0x15),
QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_SIGDET_CTRL2, 0x6d),
QMP_PHY_INIT_CFG(QPHY_V2_PCS_TX_LARGE_AMP_DRV_LVL, 0x0f),
@@ -266,7 +266,7 @@ static const struct qmp_phy_init_tbl sm6115_ufsphy_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_MIN_HIBERN8_TIME, 0x9a), /* 8 us */
};
-static const struct qmp_phy_init_tbl sdm845_ufsphy_serdes_tbl[] = {
+static const struct qmp_phy_init_tbl sdm845_ufsphy_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a),
@@ -308,13 +308,13 @@ static const struct qmp_phy_init_tbl sdm845_ufsphy_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x44),
};
-static const struct qmp_phy_init_tbl sdm845_ufsphy_tx_tbl[] = {
+static const struct qmp_phy_init_tbl sdm845_ufsphy_tx[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x07),
};
-static const struct qmp_phy_init_tbl sdm845_ufsphy_rx_tbl[] = {
+static const struct qmp_phy_init_tbl sdm845_ufsphy_rx[] = {
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),
@@ -333,7 +333,7 @@ static const struct qmp_phy_init_tbl sdm845_ufsphy_rx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x59),
};
-static const struct qmp_phy_init_tbl sdm845_ufsphy_pcs_tbl[] = {
+static const struct qmp_phy_init_tbl sdm845_ufsphy_pcs[] = {
QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_RX_SIGDET_CTRL2, 0x6e),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0a),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_TX_SMALL_AMP_DRV_LVL, 0x02),
@@ -344,7 +344,7 @@ static const struct qmp_phy_init_tbl sdm845_ufsphy_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
};
-static const struct qmp_phy_init_tbl sm8150_ufsphy_serdes_tbl[] = {
+static const struct qmp_phy_init_tbl sm8150_ufsphy_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0xd9),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x11),
QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL, 0x00),
@@ -374,7 +374,7 @@ static const struct qmp_phy_init_tbl sm8150_ufsphy_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x06),
};
-static const struct qmp_phy_init_tbl sm8150_ufsphy_tx_tbl[] = {
+static const struct qmp_phy_init_tbl sm8150_ufsphy_tx[] = {
QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1, 0x03),
QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1, 0x01),
@@ -383,7 +383,7 @@ static const struct qmp_phy_init_tbl sm8150_ufsphy_tx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V4_TX_TRAN_DRVR_EMP_EN, 0x0c),
};
-static const struct qmp_phy_init_tbl sm8150_ufsphy_rx_tbl[] = {
+static const struct qmp_phy_init_tbl sm8150_ufsphy_rx[] = {
QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_LVL, 0x24),
QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x1e),
@@ -421,7 +421,7 @@ static const struct qmp_phy_init_tbl sm8150_ufsphy_rx_tbl[] = {
};
-static const struct qmp_phy_init_tbl sm8150_ufsphy_pcs_tbl[] = {
+static const struct qmp_phy_init_tbl sm8150_ufsphy_pcs[] = {
QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_RX_SIGDET_CTRL2, 0x6d),
QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0a),
QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_SMALL_AMP_DRV_LVL, 0x02),
@@ -431,7 +431,7 @@ static const struct qmp_phy_init_tbl sm8150_ufsphy_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
};
-static const struct qmp_phy_init_tbl sm8350_ufsphy_serdes_tbl[] = {
+static const struct qmp_phy_init_tbl sm8350_ufsphy_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0xd9),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x11),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_HS_SWITCH_SEL, 0x00),
@@ -461,7 +461,7 @@ static const struct qmp_phy_init_tbl sm8350_ufsphy_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAP, 0x06),
};
-static const struct qmp_phy_init_tbl sm8350_ufsphy_tx_tbl[] = {
+static const struct qmp_phy_init_tbl sm8350_ufsphy_tx[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_TX_PWM_GEAR_1_DIVIDER_BAND0_1, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V5_TX_PWM_GEAR_2_DIVIDER_BAND0_1, 0x03),
QMP_PHY_INIT_CFG(QSERDES_V5_TX_PWM_GEAR_3_DIVIDER_BAND0_1, 0x01),
@@ -473,7 +473,7 @@ static const struct qmp_phy_init_tbl sm8350_ufsphy_tx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_TX_TRAN_DRVR_EMP_EN, 0x0c),
};
-static const struct qmp_phy_init_tbl sm8350_ufsphy_rx_tbl[] = {
+static const struct qmp_phy_init_tbl sm8350_ufsphy_rx[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_LVL, 0x24),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_CNTRL, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_DEGLITCH_CNTRL, 0x1e),
@@ -513,7 +513,7 @@ static const struct qmp_phy_init_tbl sm8350_ufsphy_rx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_RX_DCC_CTRL1, 0x0c),
};
-static const struct qmp_phy_init_tbl sm8350_ufsphy_pcs_tbl[] = {
+static const struct qmp_phy_init_tbl sm8350_ufsphy_pcs[] = {
QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_RX_SIGDET_CTRL2, 0x6d),
QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0a),
QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TX_SMALL_AMP_DRV_LVL, 0x02),
@@ -531,19 +531,24 @@ static const struct qmp_phy_init_tbl sm8350_ufsphy_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
};
+struct qmp_phy_cfg_tables {
+ /* Init sequence for PHY blocks - serdes, tx, rx, pcs */
+ const struct qmp_phy_init_tbl *serdes;
+ int serdes_num;
+ const struct qmp_phy_init_tbl *tx;
+ int tx_num;
+ const struct qmp_phy_init_tbl *rx;
+ int rx_num;
+ const struct qmp_phy_init_tbl *pcs;
+ int pcs_num;
+};
+
/* struct qmp_phy_cfg - per-PHY initialization config */
struct qmp_phy_cfg {
int lanes;
- /* Init sequence for PHY blocks - serdes, tx, rx, pcs */
- const struct qmp_phy_init_tbl *serdes_tbl;
- int serdes_tbl_num;
- const struct qmp_phy_init_tbl *tx_tbl;
- int tx_tbl_num;
- const struct qmp_phy_init_tbl *rx_tbl;
- int rx_tbl_num;
- const struct qmp_phy_init_tbl *pcs_tbl;
- int pcs_tbl_num;
+ /* Main init sequence for PHY blocks - serdes, tx, rx, pcs */
+ const struct qmp_phy_cfg_tables tables;
/* clock ids to be requested */
const char * const *clk_list;
@@ -660,12 +665,14 @@ static const char * const qmp_phy_vreg_l[] = {
static const struct qmp_phy_cfg msm8996_ufs_cfg = {
.lanes = 1,
- .serdes_tbl = msm8996_ufs_serdes_tbl,
- .serdes_tbl_num = ARRAY_SIZE(msm8996_ufs_serdes_tbl),
- .tx_tbl = msm8996_ufs_tx_tbl,
- .tx_tbl_num = ARRAY_SIZE(msm8996_ufs_tx_tbl),
- .rx_tbl = msm8996_ufs_rx_tbl,
- .rx_tbl_num = ARRAY_SIZE(msm8996_ufs_rx_tbl),
+ .tables = {
+ .serdes = msm8996_ufs_serdes,
+ .serdes_num = ARRAY_SIZE(msm8996_ufs_serdes),
+ .tx = msm8996_ufs_tx,
+ .tx_num = ARRAY_SIZE(msm8996_ufs_tx),
+ .rx = msm8996_ufs_rx,
+ .rx_num = ARRAY_SIZE(msm8996_ufs_rx),
+ },
.clk_list = msm8996_ufs_phy_clk_l,
.num_clks = ARRAY_SIZE(msm8996_ufs_phy_clk_l),
@@ -685,14 +692,16 @@ static const struct qmp_phy_cfg msm8996_ufs_cfg = {
static const struct qmp_phy_cfg sdm845_ufsphy_cfg = {
.lanes = 2,
- .serdes_tbl = sdm845_ufsphy_serdes_tbl,
- .serdes_tbl_num = ARRAY_SIZE(sdm845_ufsphy_serdes_tbl),
- .tx_tbl = sdm845_ufsphy_tx_tbl,
- .tx_tbl_num = ARRAY_SIZE(sdm845_ufsphy_tx_tbl),
- .rx_tbl = sdm845_ufsphy_rx_tbl,
- .rx_tbl_num = ARRAY_SIZE(sdm845_ufsphy_rx_tbl),
- .pcs_tbl = sdm845_ufsphy_pcs_tbl,
- .pcs_tbl_num = ARRAY_SIZE(sdm845_ufsphy_pcs_tbl),
+ .tables = {
+ .serdes = sdm845_ufsphy_serdes,
+ .serdes_num = ARRAY_SIZE(sdm845_ufsphy_serdes),
+ .tx = sdm845_ufsphy_tx,
+ .tx_num = ARRAY_SIZE(sdm845_ufsphy_tx),
+ .rx = sdm845_ufsphy_rx,
+ .rx_num = ARRAY_SIZE(sdm845_ufsphy_rx),
+ .pcs = sdm845_ufsphy_pcs,
+ .pcs_num = ARRAY_SIZE(sdm845_ufsphy_pcs),
+ },
.clk_list = sdm845_ufs_phy_clk_l,
.num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
.vreg_list = qmp_phy_vreg_l,
@@ -709,14 +718,16 @@ static const struct qmp_phy_cfg sdm845_ufsphy_cfg = {
static const struct qmp_phy_cfg sm6115_ufsphy_cfg = {
.lanes = 1,
- .serdes_tbl = sm6115_ufsphy_serdes_tbl,
- .serdes_tbl_num = ARRAY_SIZE(sm6115_ufsphy_serdes_tbl),
- .tx_tbl = sm6115_ufsphy_tx_tbl,
- .tx_tbl_num = ARRAY_SIZE(sm6115_ufsphy_tx_tbl),
- .rx_tbl = sm6115_ufsphy_rx_tbl,
- .rx_tbl_num = ARRAY_SIZE(sm6115_ufsphy_rx_tbl),
- .pcs_tbl = sm6115_ufsphy_pcs_tbl,
- .pcs_tbl_num = ARRAY_SIZE(sm6115_ufsphy_pcs_tbl),
+ .tables = {
+ .serdes = sm6115_ufsphy_serdes,
+ .serdes_num = ARRAY_SIZE(sm6115_ufsphy_serdes),
+ .tx = sm6115_ufsphy_tx,
+ .tx_num = ARRAY_SIZE(sm6115_ufsphy_tx),
+ .rx = sm6115_ufsphy_rx,
+ .rx_num = ARRAY_SIZE(sm6115_ufsphy_rx),
+ .pcs = sm6115_ufsphy_pcs,
+ .pcs_num = ARRAY_SIZE(sm6115_ufsphy_pcs),
+ },
.clk_list = sdm845_ufs_phy_clk_l,
.num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
.vreg_list = qmp_phy_vreg_l,
@@ -732,14 +743,16 @@ static const struct qmp_phy_cfg sm6115_ufsphy_cfg = {
static const struct qmp_phy_cfg sm8150_ufsphy_cfg = {
.lanes = 2,
- .serdes_tbl = sm8150_ufsphy_serdes_tbl,
- .serdes_tbl_num = ARRAY_SIZE(sm8150_ufsphy_serdes_tbl),
- .tx_tbl = sm8150_ufsphy_tx_tbl,
- .tx_tbl_num = ARRAY_SIZE(sm8150_ufsphy_tx_tbl),
- .rx_tbl = sm8150_ufsphy_rx_tbl,
- .rx_tbl_num = ARRAY_SIZE(sm8150_ufsphy_rx_tbl),
- .pcs_tbl = sm8150_ufsphy_pcs_tbl,
- .pcs_tbl_num = ARRAY_SIZE(sm8150_ufsphy_pcs_tbl),
+ .tables = {
+ .serdes = sm8150_ufsphy_serdes,
+ .serdes_num = ARRAY_SIZE(sm8150_ufsphy_serdes),
+ .tx = sm8150_ufsphy_tx,
+ .tx_num = ARRAY_SIZE(sm8150_ufsphy_tx),
+ .rx = sm8150_ufsphy_rx,
+ .rx_num = ARRAY_SIZE(sm8150_ufsphy_rx),
+ .pcs = sm8150_ufsphy_pcs,
+ .pcs_num = ARRAY_SIZE(sm8150_ufsphy_pcs),
+ },
.clk_list = sdm845_ufs_phy_clk_l,
.num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
.vreg_list = qmp_phy_vreg_l,
@@ -754,14 +767,16 @@ static const struct qmp_phy_cfg sm8150_ufsphy_cfg = {
static const struct qmp_phy_cfg sm8350_ufsphy_cfg = {
.lanes = 2,
- .serdes_tbl = sm8350_ufsphy_serdes_tbl,
- .serdes_tbl_num = ARRAY_SIZE(sm8350_ufsphy_serdes_tbl),
- .tx_tbl = sm8350_ufsphy_tx_tbl,
- .tx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_tx_tbl),
- .rx_tbl = sm8350_ufsphy_rx_tbl,
- .rx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_rx_tbl),
- .pcs_tbl = sm8350_ufsphy_pcs_tbl,
- .pcs_tbl_num = ARRAY_SIZE(sm8350_ufsphy_pcs_tbl),
+ .tables = {
+ .serdes = sm8350_ufsphy_serdes,
+ .serdes_num = ARRAY_SIZE(sm8350_ufsphy_serdes),
+ .tx = sm8350_ufsphy_tx,
+ .tx_num = ARRAY_SIZE(sm8350_ufsphy_tx),
+ .rx = sm8350_ufsphy_rx,
+ .rx_num = ARRAY_SIZE(sm8350_ufsphy_rx),
+ .pcs = sm8350_ufsphy_pcs,
+ .pcs_num = ARRAY_SIZE(sm8350_ufsphy_pcs),
+ },
.clk_list = sdm845_ufs_phy_clk_l,
.num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
.vreg_list = qmp_phy_vreg_l,
@@ -776,14 +791,16 @@ static const struct qmp_phy_cfg sm8350_ufsphy_cfg = {
static const struct qmp_phy_cfg sm8450_ufsphy_cfg = {
.lanes = 2,
- .serdes_tbl = sm8350_ufsphy_serdes_tbl,
- .serdes_tbl_num = ARRAY_SIZE(sm8350_ufsphy_serdes_tbl),
- .tx_tbl = sm8350_ufsphy_tx_tbl,
- .tx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_tx_tbl),
- .rx_tbl = sm8350_ufsphy_rx_tbl,
- .rx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_rx_tbl),
- .pcs_tbl = sm8350_ufsphy_pcs_tbl,
- .pcs_tbl_num = ARRAY_SIZE(sm8350_ufsphy_pcs_tbl),
+ .tables = {
+ .serdes = sm8350_ufsphy_serdes,
+ .serdes_num = ARRAY_SIZE(sm8350_ufsphy_serdes),
+ .tx = sm8350_ufsphy_tx,
+ .tx_num = ARRAY_SIZE(sm8350_ufsphy_tx),
+ .rx = sm8350_ufsphy_rx,
+ .rx_num = ARRAY_SIZE(sm8350_ufsphy_rx),
+ .pcs = sm8350_ufsphy_pcs,
+ .pcs_num = ARRAY_SIZE(sm8350_ufsphy_pcs),
+ },
.clk_list = sm8450_ufs_phy_clk_l,
.num_clks = ARRAY_SIZE(sm8450_ufs_phy_clk_l),
.vreg_list = qmp_phy_vreg_l,
@@ -826,16 +843,43 @@ static void qmp_ufs_configure(void __iomem *base,
qmp_ufs_configure_lane(base, regs, tbl, num, 0xff);
}
-static int qmp_ufs_serdes_init(struct qmp_phy *qphy)
+static void qmp_ufs_serdes_init(struct qmp_phy *qphy, const struct qmp_phy_cfg_tables *tables)
{
const struct qmp_phy_cfg *cfg = qphy->cfg;
void __iomem *serdes = qphy->serdes;
- const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl;
- int serdes_tbl_num = cfg->serdes_tbl_num;
- qmp_ufs_configure(serdes, cfg->regs, serdes_tbl, serdes_tbl_num);
+ if (!tables)
+ return;
- return 0;
+ qmp_ufs_configure(serdes, cfg->regs, tables->serdes, tables->serdes_num);
+}
+
+static void qmp_ufs_lanes_init(struct qmp_phy *qphy, const struct qmp_phy_cfg_tables *tables)
+{
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *tx = qphy->tx;
+ void __iomem *rx = qphy->rx;
+
+ qmp_ufs_configure_lane(tx, cfg->regs, tables->tx, tables->tx_num, 1);
+
+ if (cfg->lanes >= 2)
+ qmp_ufs_configure_lane(qphy->tx2, cfg->regs, tables->tx, tables->tx_num, 2);
+
+ qmp_ufs_configure_lane(rx, cfg->regs, tables->rx, tables->rx_num, 1);
+
+ if (cfg->lanes >= 2)
+ qmp_ufs_configure_lane(qphy->rx2, cfg->regs, tables->rx, tables->rx_num, 2);
+}
+
+static void qmp_ufs_pcs_init(struct qmp_phy *qphy, const struct qmp_phy_cfg_tables *tables)
+{
+ const struct qmp_phy_cfg *cfg = qphy->cfg;
+ void __iomem *pcs = qphy->pcs;
+
+ if (!tables)
+ return;
+
+ qmp_ufs_configure(pcs, cfg->regs, tables->pcs, tables->pcs_num);
}
static int qmp_ufs_com_init(struct qmp_phy *qphy)
@@ -933,31 +977,16 @@ static int qmp_ufs_power_on(struct phy *phy)
struct qmp_phy *qphy = phy_get_drvdata(phy);
struct qcom_qmp *qmp = qphy->qmp;
const struct qmp_phy_cfg *cfg = qphy->cfg;
- void __iomem *tx = qphy->tx;
- void __iomem *rx = qphy->rx;
void __iomem *pcs = qphy->pcs;
void __iomem *status;
unsigned int mask, val, ready;
int ret;
- qmp_ufs_serdes_init(qphy);
-
- /* Tx, Rx, and PCS configurations */
- qmp_ufs_configure_lane(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num, 1);
+ qmp_ufs_serdes_init(qphy, &cfg->tables);
- if (cfg->lanes >= 2) {
- qmp_ufs_configure_lane(qphy->tx2, cfg->regs,
- cfg->tx_tbl, cfg->tx_tbl_num, 2);
- }
-
- qmp_ufs_configure_lane(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num, 1);
-
- if (cfg->lanes >= 2) {
- qmp_ufs_configure_lane(qphy->rx2, cfg->regs,
- cfg->rx_tbl, cfg->rx_tbl_num, 2);
- }
+ qmp_ufs_lanes_init(qphy, &cfg->tables);
- qmp_ufs_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
+ qmp_ufs_pcs_init(qphy, &cfg->tables);
ret = reset_control_deassert(qmp->ufs_reset);
if (ret)
--
2.25.1
On newer UFS revisions, the register at offset 0xD0 is called,
REG_UFS_PARAM0. Since the existing register, RETRY_TIMER_REG is not used
anywhere, it is safe to use the new name.
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/ufs/host/ufs-qcom.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h
index 9d96ac71b27f..7fe928b82753 100644
--- a/drivers/ufs/host/ufs-qcom.h
+++ b/drivers/ufs/host/ufs-qcom.h
@@ -33,7 +33,8 @@ enum {
REG_UFS_TX_SYMBOL_CLK_NS_US = 0xC4,
REG_UFS_LOCAL_PORT_ID_REG = 0xC8,
REG_UFS_PA_ERR_CODE = 0xCC,
- REG_UFS_RETRY_TIMER_REG = 0xD0,
+ /* On older UFS revisions, this register is called "RETRY_TIMER_REG" */
+ REG_UFS_PARAM0 = 0xD0,
REG_UFS_PA_LINK_STARTUP_TIMER = 0xD8,
REG_UFS_CFG1 = 0xDC,
REG_UFS_CFG2 = 0xE0,
--
2.25.1
Make use of dev_err_probe() for printing the probe error.
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/ufs/host/ufs-qcom.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
index 8bb0f4415f1a..38e2ed749d75 100644
--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -1441,9 +1441,9 @@ static int ufs_qcom_probe(struct platform_device *pdev)
/* Perform generic probe */
err = ufshcd_pltfrm_init(pdev, &ufs_hba_qcom_vops);
if (err)
- dev_err(dev, "ufshcd_pltfrm_init() failed %d\n", err);
+ return dev_err_probe(dev, err, "ufshcd_pltfrm_init() failed\n");
- return err;
+ return 0;
}
/**
--
2.25.1
Starting from UFS controller v4, Qcom supports dual gear mode (i.e., the
controller/PHY can be configured to run in two gear speeds). But that
requires an agreement between the UFS controller and the UFS device.
This commit finds the max gear supported by both controller and device
then decides which one to use.
UFS controller's max gear can be read from the REG_UFS_PARAM0 register and
UFS device's max gear can be read from the "max-device-gear" devicetree
property.
The UFS PHY also needs to be configured with the decided gear using the
phy_set_mode_ext() API.
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/ufs/host/ufs-qcom.c | 31 ++++++++++++++++++++++++++++---
drivers/ufs/host/ufs-qcom.h | 4 ++++
2 files changed, 32 insertions(+), 3 deletions(-)
diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
index c93d2d38b43e..ca60a5b0292b 100644
--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -281,6 +281,9 @@ static int ufs_qcom_host_reset(struct ufs_hba *hba)
static u32 ufs_qcom_get_hs_gear(struct ufs_hba *hba, u32 hs_gear)
{
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
+ struct device *dev = hba->dev;
+ u32 max_device_gear, max_hcd_gear, reg;
+ int ret;
if (host->hw_ver.major == 0x1) {
/*
@@ -292,8 +295,29 @@ static u32 ufs_qcom_get_hs_gear(struct ufs_hba *hba, u32 hs_gear)
*/
if (hs_gear > UFS_HS_G2)
return UFS_HS_G2;
+ } else if (host->hw_ver.major > 0x3) {
+ /*
+ * Starting from UFS controller v4, Qcom supports dual gear mode (i.e., the
+ * controller/PHY can be configured to run in two gear speeds). But that
+ * requires an agreement between the UFS controller and the device. Below
+ * code tries to find the max gear of both and decides which gear to use.
+ *
+ * First get the max gear supported by the UFS device if available.
+ * If the property is not defined in devicetree, then use the default gear.
+ */
+ ret = of_property_read_u32(dev->of_node, "max-device-gear", &max_device_gear);
+ if (ret)
+ goto err_out;
+
+ /* Next get the max gear supported by the UFS controller */
+ reg = ufshcd_readl(hba, REG_UFS_PARAM0);
+ max_hcd_gear = UFS_QCOM_MAX_GEAR(reg);
+
+ /* Now return the minimum of both gears */
+ return min(max_device_gear, max_hcd_gear);
}
+err_out:
/* Default is HS-G3 */
return UFS_HS_G3;
}
@@ -303,7 +327,7 @@ 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;
int ret;
- bool is_rate_B = UFS_QCOM_LIMIT_HS_RATE == PA_HS_MODE_B;
+ u32 hs_gear;
/* Reset UFS Host Controller and PHY */
ret = ufs_qcom_host_reset(hba);
@@ -311,8 +335,9 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
dev_warn(hba->dev, "%s: host reset returned %d\n",
__func__, ret);
- if (is_rate_B)
- phy_set_mode(phy, PHY_MODE_UFS_HS_B);
+ /* UFS_HS_G2 is used here since that's the least gear supported by legacy Qcom platforms */
+ hs_gear = ufs_qcom_get_hs_gear(hba, UFS_HS_G2);
+ phy_set_mode_ext(phy, PHY_MODE_UFS_HS_B, hs_gear);
/* phy initialization - calibrate the phy */
ret = phy_init(phy);
diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h
index 7fe928b82753..751ded3e3531 100644
--- a/drivers/ufs/host/ufs-qcom.h
+++ b/drivers/ufs/host/ufs-qcom.h
@@ -94,6 +94,10 @@ enum {
#define TMRLUT_HW_CGC_EN BIT(6)
#define OCSC_HW_CGC_EN BIT(7)
+/* bit definitions for REG_UFS_PARAM0 */
+#define MAX_HS_GEAR_MASK GENMASK(6, 4)
+#define UFS_QCOM_MAX_GEAR(x) FIELD_GET(MAX_HS_GEAR_MASK, (x))
+
/* bit definition for UFS_UFS_TEST_BUS_CTRL_n */
#define TEST_BUS_SUB_SEL_MASK GENMASK(4, 0) /* All XXX_SEL fields are 5 bits wide */
--
2.25.1
Add separate tables_hs_b instance to allow the PHY driver to configure the
PHY in HS Series B mode. The individual SoC configs need to supply the
serdes register setting in tables_hs_b and the UFS driver can request the
Series B mode by calling phy_set_mode() with mode set to PHY_MODE_UFS_HS_B.
Reviewed-by: Dmitry Baryshkov <[email protected]>
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
index cdfda4e6d575..4c6a2b5afc9a 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
@@ -20,6 +20,8 @@
#include <linux/reset.h>
#include <linux/slab.h>
+#include <ufs/unipro.h>
+
#include <dt-bindings/phy/phy.h>
#include "phy-qcom-qmp.h"
@@ -549,6 +551,8 @@ struct qmp_phy_cfg {
/* Main init sequence for PHY blocks - serdes, tx, rx, pcs */
const struct qmp_phy_cfg_tables tables;
+ /* Additional sequence for HS Series B */
+ const struct qmp_phy_cfg_tables tables_hs_b;
/* clock ids to be requested */
const char * const *clk_list;
@@ -582,6 +586,7 @@ struct qmp_phy_cfg {
* @rx2: iomapped memory space for second lane's rx (in dual lane PHYs)
* @pcs_misc: iomapped memory space for lane's pcs_misc
* @qmp: QMP phy to which this lane belongs
+ * @mode: PHY mode configured by the UFS driver
*/
struct qmp_phy {
struct phy *phy;
@@ -594,6 +599,7 @@ struct qmp_phy {
void __iomem *rx2;
void __iomem *pcs_misc;
struct qcom_qmp *qmp;
+ u32 mode;
};
/**
@@ -983,6 +989,8 @@ static int qmp_ufs_power_on(struct phy *phy)
int ret;
qmp_ufs_serdes_init(qphy, &cfg->tables);
+ if (qphy->mode == PHY_MODE_UFS_HS_B)
+ qmp_ufs_serdes_init(qphy, &cfg->tables_hs_b);
qmp_ufs_lanes_init(qphy, &cfg->tables);
@@ -1070,6 +1078,15 @@ static int qmp_ufs_disable(struct phy *phy)
return qmp_ufs_exit(phy);
}
+static int qmp_ufs_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+
+ qphy->mode = mode;
+
+ return 0;
+}
+
static int qmp_ufs_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg)
{
struct qcom_qmp *qmp = dev_get_drvdata(dev);
@@ -1105,6 +1122,7 @@ static int qmp_ufs_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg)
static const struct phy_ops qcom_qmp_ufs_ops = {
.power_on = qmp_ufs_enable,
.power_off = qmp_ufs_disable,
+ .set_mode = qmp_ufs_set_mode,
.owner = THIS_MODULE,
};
--
2.25.1
In the reset assert and deassert callbacks, the supplied "id" is not used
at all and only the hba reset is performed all the time. So there is no
reason to use a WARN_ON on the "id".
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/ufs/host/ufs-qcom.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
index 7cd996ac180b..8bb0f4415f1a 100644
--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -895,8 +895,6 @@ ufs_qcom_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
{
struct ufs_qcom_host *host = rcdev_to_ufs_host(rcdev);
- /* Currently this code only knows about a single reset. */
- WARN_ON(id);
ufs_qcom_assert_reset(host->hba);
/* provide 1ms delay to let the reset pulse propagate. */
usleep_range(1000, 1100);
@@ -908,8 +906,6 @@ ufs_qcom_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
{
struct ufs_qcom_host *host = rcdev_to_ufs_host(rcdev);
- /* Currently this code only knows about a single reset. */
- WARN_ON(id);
ufs_qcom_deassert_reset(host->hba);
/*
--
2.25.1
UFS PHY in SM8250 SoC is capable of operating at HS G4 mode. Hence, add the
required register settings using the tables_hs_g4 struct instance. This
also requires a separate qmp_phy_cfg for SM8250 instead of SM8150.
Reviewed-by: Dmitry Baryshkov <[email protected]>
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 68 ++++++++++++++++++++++++-
1 file changed, 67 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
index 5f2a012707b7..fa7457c0202b 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
@@ -385,6 +385,10 @@ static const struct qmp_phy_init_tbl sm8150_ufsphy_tx[] = {
QMP_PHY_INIT_CFG(QSERDES_V4_TX_TRAN_DRVR_EMP_EN, 0x0c),
};
+static const struct qmp_phy_init_tbl sm8250_ufsphy_hs_g4_tx[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xe5),
+};
+
static const struct qmp_phy_init_tbl sm8150_ufsphy_rx[] = {
QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_LVL, 0x24),
QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x0f),
@@ -420,7 +424,32 @@ static const struct qmp_phy_init_tbl sm8150_ufsphy_rx[] = {
QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xc8),
QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x3b),
QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0xb1),
+};
+static const struct qmp_phy_init_tbl sm8250_ufsphy_hs_g4_rx[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x5a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CTRL2, 0x81),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_TERM_BW, 0x6f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x17),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_MEASURE_TIME, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x2c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0x6d),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0x6d),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xed),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0x3c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x0f),
};
static const struct qmp_phy_init_tbl sm8150_ufsphy_pcs[] = {
@@ -433,6 +462,11 @@ static const struct qmp_phy_init_tbl sm8150_ufsphy_pcs[] = {
QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
};
+static const struct qmp_phy_init_tbl sm8250_ufsphy_hs_g4_pcs[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x10),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_BIST_FIXED_PAT_CTRL, 0x0a),
+};
+
static const struct qmp_phy_init_tbl sm8350_ufsphy_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0xd9),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x11),
@@ -774,6 +808,38 @@ static const struct qmp_phy_cfg sm8150_ufsphy_cfg = {
.phy_status = PHYSTATUS,
};
+static const struct qmp_phy_cfg sm8250_ufsphy_cfg = {
+ .lanes = 2,
+
+ .tables = {
+ .serdes = sm8150_ufsphy_serdes,
+ .serdes_num = ARRAY_SIZE(sm8150_ufsphy_serdes),
+ .tx = sm8150_ufsphy_tx,
+ .tx_num = ARRAY_SIZE(sm8150_ufsphy_tx),
+ .rx = sm8150_ufsphy_rx,
+ .rx_num = ARRAY_SIZE(sm8150_ufsphy_rx),
+ .pcs = sm8150_ufsphy_pcs,
+ .pcs_num = ARRAY_SIZE(sm8150_ufsphy_pcs),
+ },
+ .tables_hs_g4 = {
+ .tx = sm8250_ufsphy_hs_g4_tx,
+ .tx_num = ARRAY_SIZE(sm8250_ufsphy_hs_g4_tx),
+ .rx = sm8250_ufsphy_hs_g4_rx,
+ .rx_num = ARRAY_SIZE(sm8250_ufsphy_hs_g4_rx),
+ .pcs = sm8250_ufsphy_hs_g4_pcs,
+ .pcs_num = ARRAY_SIZE(sm8250_ufsphy_hs_g4_pcs),
+ },
+ .clk_list = sdm845_ufs_phy_clk_l,
+ .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = sm8150_ufsphy_regs_layout,
+
+ .start_ctrl = SERDES_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .phy_status = PHYSTATUS,
+};
+
static const struct qmp_phy_cfg sm8350_ufsphy_cfg = {
.lanes = 2,
@@ -1226,7 +1292,7 @@ static const struct of_device_id qmp_ufs_of_match_table[] = {
.data = &sm8150_ufsphy_cfg,
}, {
.compatible = "qcom,sm8250-qmp-ufs-phy",
- .data = &sm8150_ufsphy_cfg,
+ .data = &sm8250_ufsphy_cfg,
}, {
.compatible = "qcom,sm8350-qmp-ufs-phy",
.data = &sm8350_ufsphy_cfg,
--
2.25.1
On Mon, Oct 31, 2022 at 11:32:11PM +0530, Manivannan Sadhasivam wrote:
> In the reset assert and deassert callbacks, the supplied "id" is not used
> at all and only the hba reset is performed all the time. So there is no
> reason to use a WARN_ON on the "id".
>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>
Reviewed-by: Andrew Halaney <[email protected]>
> ---
> drivers/ufs/host/ufs-qcom.c | 4 ----
> 1 file changed, 4 deletions(-)
>
> diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
> index 7cd996ac180b..8bb0f4415f1a 100644
> --- a/drivers/ufs/host/ufs-qcom.c
> +++ b/drivers/ufs/host/ufs-qcom.c
> @@ -895,8 +895,6 @@ ufs_qcom_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
> {
> struct ufs_qcom_host *host = rcdev_to_ufs_host(rcdev);
>
> - /* Currently this code only knows about a single reset. */
> - WARN_ON(id);
> ufs_qcom_assert_reset(host->hba);
> /* provide 1ms delay to let the reset pulse propagate. */
> usleep_range(1000, 1100);
> @@ -908,8 +906,6 @@ ufs_qcom_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
> {
> struct ufs_qcom_host *host = rcdev_to_ufs_host(rcdev);
>
> - /* Currently this code only knows about a single reset. */
> - WARN_ON(id);
> ufs_qcom_deassert_reset(host->hba);
>
> /*
> --
> 2.25.1
>
On Mon, Oct 31, 2022 at 11:32:13PM +0530, Manivannan Sadhasivam wrote:
> Make use of dev_err_probe() for printing the probe error.
>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>
Reviewed-by: Andrew Halaney <[email protected]>
> ---
> drivers/ufs/host/ufs-qcom.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
> index 8bb0f4415f1a..38e2ed749d75 100644
> --- a/drivers/ufs/host/ufs-qcom.c
> +++ b/drivers/ufs/host/ufs-qcom.c
> @@ -1441,9 +1441,9 @@ static int ufs_qcom_probe(struct platform_device *pdev)
> /* Perform generic probe */
> err = ufshcd_pltfrm_init(pdev, &ufs_hba_qcom_vops);
> if (err)
> - dev_err(dev, "ufshcd_pltfrm_init() failed %d\n", err);
> + return dev_err_probe(dev, err, "ufshcd_pltfrm_init() failed\n");
>
> - return err;
> + return 0;
> }
>
> /**
> --
> 2.25.1
>
On Mon, Oct 31, 2022 at 11:32:16PM +0530, Manivannan Sadhasivam wrote:
> Starting from UFS controller v4, Qcom supports dual gear mode (i.e., the
> controller/PHY can be configured to run in two gear speeds). But that
> requires an agreement between the UFS controller and the UFS device.
> This commit finds the max gear supported by both controller and device
> then decides which one to use.
>
> UFS controller's max gear can be read from the REG_UFS_PARAM0 register and
> UFS device's max gear can be read from the "max-device-gear" devicetree
> property.
>
> The UFS PHY also needs to be configured with the decided gear using the
> phy_set_mode_ext() API.
>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>
> ---
> drivers/ufs/host/ufs-qcom.c | 31 ++++++++++++++++++++++++++++---
> drivers/ufs/host/ufs-qcom.h | 4 ++++
> 2 files changed, 32 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
> index c93d2d38b43e..ca60a5b0292b 100644
> --- a/drivers/ufs/host/ufs-qcom.c
> +++ b/drivers/ufs/host/ufs-qcom.c
> @@ -281,6 +281,9 @@ static int ufs_qcom_host_reset(struct ufs_hba *hba)
> static u32 ufs_qcom_get_hs_gear(struct ufs_hba *hba, u32 hs_gear)
> {
> struct ufs_qcom_host *host = ufshcd_get_variant(hba);
> + struct device *dev = hba->dev;
> + u32 max_device_gear, max_hcd_gear, reg;
> + int ret;
>
> if (host->hw_ver.major == 0x1) {
> /*
> @@ -292,8 +295,29 @@ static u32 ufs_qcom_get_hs_gear(struct ufs_hba *hba, u32 hs_gear)
> */
> if (hs_gear > UFS_HS_G2)
> return UFS_HS_G2;
> + } else if (host->hw_ver.major > 0x3) {
> + /*
> + * Starting from UFS controller v4, Qcom supports dual gear mode (i.e., the
Bikeshedding, but I think with this wording checking:
host->hw_ver.major >= 0x4
is a little more readable, or at least for me when I read the comment I
had to jump back up to the else if statement.
Even without that change though
Reviewed-by: Andrew Halaney <[email protected]>
> + * controller/PHY can be configured to run in two gear speeds). But that
> + * requires an agreement between the UFS controller and the device. Below
> + * code tries to find the max gear of both and decides which gear to use.
> + *
> + * First get the max gear supported by the UFS device if available.
> + * If the property is not defined in devicetree, then use the default gear.
> + */
> + ret = of_property_read_u32(dev->of_node, "max-device-gear", &max_device_gear);
> + if (ret)
> + goto err_out;
> +
> + /* Next get the max gear supported by the UFS controller */
> + reg = ufshcd_readl(hba, REG_UFS_PARAM0);
> + max_hcd_gear = UFS_QCOM_MAX_GEAR(reg);
> +
> + /* Now return the minimum of both gears */
> + return min(max_device_gear, max_hcd_gear);
> }
>
> +err_out:
> /* Default is HS-G3 */
> return UFS_HS_G3;
> }
> @@ -303,7 +327,7 @@ 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;
> int ret;
> - bool is_rate_B = UFS_QCOM_LIMIT_HS_RATE == PA_HS_MODE_B;
> + u32 hs_gear;
>
> /* Reset UFS Host Controller and PHY */
> ret = ufs_qcom_host_reset(hba);
> @@ -311,8 +335,9 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
> dev_warn(hba->dev, "%s: host reset returned %d\n",
> __func__, ret);
>
> - if (is_rate_B)
> - phy_set_mode(phy, PHY_MODE_UFS_HS_B);
> + /* UFS_HS_G2 is used here since that's the least gear supported by legacy Qcom platforms */
> + hs_gear = ufs_qcom_get_hs_gear(hba, UFS_HS_G2);
> + phy_set_mode_ext(phy, PHY_MODE_UFS_HS_B, hs_gear);
>
> /* phy initialization - calibrate the phy */
> ret = phy_init(phy);
> diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h
> index 7fe928b82753..751ded3e3531 100644
> --- a/drivers/ufs/host/ufs-qcom.h
> +++ b/drivers/ufs/host/ufs-qcom.h
> @@ -94,6 +94,10 @@ enum {
> #define TMRLUT_HW_CGC_EN BIT(6)
> #define OCSC_HW_CGC_EN BIT(7)
>
> +/* bit definitions for REG_UFS_PARAM0 */
> +#define MAX_HS_GEAR_MASK GENMASK(6, 4)
> +#define UFS_QCOM_MAX_GEAR(x) FIELD_GET(MAX_HS_GEAR_MASK, (x))
> +
> /* bit definition for UFS_UFS_TEST_BUS_CTRL_n */
> #define TEST_BUS_SUB_SEL_MASK GENMASK(4, 0) /* All XXX_SEL fields are 5 bits wide */
>
> --
> 2.25.1
>
On Mon, Oct 31, 2022 at 11:32:15PM +0530, Manivannan Sadhasivam wrote:
> In the preparation of adding support for new gears, let's move the
> logic that finds the gear for each platform to a new function. This helps
> with code readability and also allows the logic to be used in other places
> of the driver in future.
>
> While at it, let's make it clear that this driver only supports symmetric
> gear setting (hs_tx_gear == hs_rx_gear).
>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>
Reviewed-by: Andrew Halaney <[email protected]>
> ---
> drivers/ufs/host/ufs-qcom.c | 36 +++++++++++++++++++++++-------------
> 1 file changed, 23 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
> index 38e2ed749d75..c93d2d38b43e 100644
> --- a/drivers/ufs/host/ufs-qcom.c
> +++ b/drivers/ufs/host/ufs-qcom.c
> @@ -278,6 +278,26 @@ static int ufs_qcom_host_reset(struct ufs_hba *hba)
> return 0;
> }
>
> +static u32 ufs_qcom_get_hs_gear(struct ufs_hba *hba, u32 hs_gear)
> +{
> + struct ufs_qcom_host *host = ufshcd_get_variant(hba);
> +
> + if (host->hw_ver.major == 0x1) {
> + /*
> + * HS-G3 operations may not reliably work on legacy QCOM
> + * UFS host controller hardware even though capability
> + * exchange during link startup phase may end up
> + * negotiating maximum supported gear as G3.
> + * Hence downgrade the maximum supported gear to HS-G2.
> + */
> + if (hs_gear > UFS_HS_G2)
> + return UFS_HS_G2;
> + }
> +
> + /* Default is HS-G3 */
> + return UFS_HS_G3;
> +}
> +
> static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
> {
> struct ufs_qcom_host *host = ufshcd_get_variant(hba);
> @@ -692,19 +712,9 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
> ufshcd_init_pwr_dev_param(&ufs_qcom_cap);
> ufs_qcom_cap.hs_rate = UFS_QCOM_LIMIT_HS_RATE;
>
> - if (host->hw_ver.major == 0x1) {
> - /*
> - * HS-G3 operations may not reliably work on legacy QCOM
> - * UFS host controller hardware even though capability
> - * exchange during link startup phase may end up
> - * negotiating maximum supported gear as G3.
> - * Hence downgrade the maximum supported gear to HS-G2.
> - */
> - if (ufs_qcom_cap.hs_tx_gear > UFS_HS_G2)
> - ufs_qcom_cap.hs_tx_gear = UFS_HS_G2;
> - if (ufs_qcom_cap.hs_rx_gear > UFS_HS_G2)
> - ufs_qcom_cap.hs_rx_gear = UFS_HS_G2;
> - }
> + /* This driver only supports symmetic gear setting i.e., hs_tx_gear == hs_rx_gear */
> + ufs_qcom_cap.hs_tx_gear = ufs_qcom_cap.hs_rx_gear = ufs_qcom_get_hs_gear(hba,
> + ufs_qcom_cap.hs_tx_gear);
>
> ret = ufshcd_get_pwr_dev_param(&ufs_qcom_cap,
> dev_max_params,
> --
> 2.25.1
>
On Mon, Oct 31, 2022 at 11:32:14PM +0530, Manivannan Sadhasivam wrote:
> On newer UFS revisions, the register at offset 0xD0 is called,
> REG_UFS_PARAM0. Since the existing register, RETRY_TIMER_REG is not used
> anywhere, it is safe to use the new name.
>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>
Reviewed-by: Andrew Halaney <[email protected]>
> ---
> drivers/ufs/host/ufs-qcom.h | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h
> index 9d96ac71b27f..7fe928b82753 100644
> --- a/drivers/ufs/host/ufs-qcom.h
> +++ b/drivers/ufs/host/ufs-qcom.h
> @@ -33,7 +33,8 @@ enum {
> REG_UFS_TX_SYMBOL_CLK_NS_US = 0xC4,
> REG_UFS_LOCAL_PORT_ID_REG = 0xC8,
> REG_UFS_PA_ERR_CODE = 0xCC,
> - REG_UFS_RETRY_TIMER_REG = 0xD0,
> + /* On older UFS revisions, this register is called "RETRY_TIMER_REG" */
> + REG_UFS_PARAM0 = 0xD0,
> REG_UFS_PA_LINK_STARTUP_TIMER = 0xD8,
> REG_UFS_CFG1 = 0xDC,
> REG_UFS_CFG2 = 0xE0,
> --
> 2.25.1
>
On Mon, Oct 31, 2022 at 11:32:08PM +0530, Manivannan Sadhasivam wrote:
> The maximum gear supported by the UFS device can be specified using the
> "max-device-gear" property. This allows the UFS controller to configure the
> TX/RX gear before starting communication with the UFS device.
>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>
> ---
> Documentation/devicetree/bindings/ufs/ufs-common.yaml | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/ufs/ufs-common.yaml b/Documentation/devicetree/bindings/ufs/ufs-common.yaml
> index 47a4e9e1a775..5dcd14909ad5 100644
> --- a/Documentation/devicetree/bindings/ufs/ufs-common.yaml
> +++ b/Documentation/devicetree/bindings/ufs/ufs-common.yaml
> @@ -73,6 +73,11 @@ properties:
> description:
> Specifies max. load that can be drawn from VCCQ2 supply.
>
> + max-device-gear:
Needs a type $ref
> + description:
> + Specifies max. gear the UFS device supports.
> + enum: [1, 2, 3, 4, 5]
> +
> dependencies:
> freq-table-hz: [ 'clocks' ]
>
> --
> 2.25.1
>
>
On 31/10/2022 14:02, Manivannan Sadhasivam wrote:
> The maximum gear supported by the UFS device can be specified using the
> "max-device-gear" property. This allows the UFS controller to configure the
> TX/RX gear before starting communication with the UFS device.
This is confusing. The UFS PHY provides gear capability, so what is the
"device" here? The attached memory? How could it report something else
than phy?
The last sentence also suggests that you statically encode gear to avoid
runtime negotiation.
Best regards,
Krzysztof
On Wed, Nov 02, 2022 at 03:09:50PM -0400, Krzysztof Kozlowski wrote:
> On 31/10/2022 14:02, Manivannan Sadhasivam wrote:
> > The maximum gear supported by the UFS device can be specified using the
> > "max-device-gear" property. This allows the UFS controller to configure the
> > TX/RX gear before starting communication with the UFS device.
>
> This is confusing. The UFS PHY provides gear capability, so what is the
> "device" here? The attached memory? How could it report something else
> than phy?
>
This is the norm with any storage protocol, right? Both host and device
(memory) can support different speeds and the OEM can choose to put any
combinations (even though it might not be very efficient).
For instance,
PHY (G4) -> Device (G3)
From the host perspective we know what the PHY can support but that's not the
same with the device until probing it. And probing requires using a minimum
supported gear. For sure we can use something like G2/G3 and reinit later but
as I learnt, that approach was rejected by the community when submitted
by Qualcomm earlier.
> The last sentence also suggests that you statically encode gear to avoid
> runtime negotiation.
>
Yes, the OEM should know what the max gear speed they want to run, so getting
this info from DT makes sense.
Thanks,
Mani
> Best regards,
> Krzysztof
>
--
மணிவண்ணன் சதாசிவம்
On 03/11/2022 08:28, Manivannan Sadhasivam wrote:
> On Wed, Nov 02, 2022 at 03:09:50PM -0400, Krzysztof Kozlowski wrote:
>> On 31/10/2022 14:02, Manivannan Sadhasivam wrote:
>>> The maximum gear supported by the UFS device can be specified using the
>>> "max-device-gear" property. This allows the UFS controller to configure the
>>> TX/RX gear before starting communication with the UFS device.
>>
>> This is confusing. The UFS PHY provides gear capability, so what is the
>> "device" here? The attached memory? How could it report something else
>> than phy?
>>
>
> This is the norm with any storage protocol, right? Both host and device
> (memory) can support different speeds and the OEM can choose to put any
> combinations (even though it might not be very efficient).
>
> For instance,
>
> PHY (G4) -> Device (G3)
Yes and look at MMC - no need to define "max mode" supported by eMMC.
You define the modes supported by controller but the memory capabilities
are being autodetected and negotiated.
>
> From the host perspective we know what the PHY can support but that's not the
> same with the device until probing it. And probing requires using a minimum
> supported gear. For sure we can use something like G2/G3 and reinit later but
> as I learnt, that approach was rejected by the community when submitted
> by Qualcomm earlier.
It should be then referenced somewhere as it might be a reason to accept
the property.
>
>> The last sentence also suggests that you statically encode gear to avoid
>> runtime negotiation.
>>
>
> Yes, the OEM should know what the max gear speed they want to run, so getting
> this info from DT makes sense.
Not really if it is auto-detectable. Just because things are static is
not the sole reason to put them into DT. The reason is - they are not
detectable by OS/firmware thus we must have them in DT to be able to
know it.
Best regards,
Krzysztof
On 31-10-22, 23:32, Manivannan Sadhasivam wrote:
> As done for Qcom PCIe PHY driver, let's move the register settings to the
> common qmp_phy_cfg_tables struct. This helps in adding any additional PHY
> settings needed for functionalities like HS-G4 in the future by adding one
> more instance of the qmp_phy_cfg_tables.
Good idea..
>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>
> ---
> drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 223 +++++++++++++-----------
> 1 file changed, 126 insertions(+), 97 deletions(-)
>
> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
> index c08d34ad1313..cdfda4e6d575 100644
> --- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
> @@ -107,7 +107,7 @@ static const unsigned int sm8150_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = {
> [QPHY_SW_RESET] = QPHY_V4_PCS_UFS_SW_RESET,
> };
>
> -static const struct qmp_phy_init_tbl msm8996_ufs_serdes_tbl[] = {
> +static const struct qmp_phy_init_tbl msm8996_ufs_serdes[] = {
Ugh, this is a change not per the log above.
It is okay to do this change, but should be probably a separate patch..
> QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x0e),
> QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0xd7),
> QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
> @@ -156,12 +156,12 @@ static const struct qmp_phy_init_tbl msm8996_ufs_serdes_tbl[] = {
> QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE1, 0x00),
> };
>
> -static const struct qmp_phy_init_tbl msm8996_ufs_tx_tbl[] = {
> +static const struct qmp_phy_init_tbl msm8996_ufs_tx[] = {
> QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
> QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x02),
> };
>
> -static const struct qmp_phy_init_tbl msm8996_ufs_rx_tbl[] = {
> +static const struct qmp_phy_init_tbl msm8996_ufs_rx[] = {
> QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x24),
> QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x02),
> QMP_PHY_INIT_CFG(QSERDES_RX_RX_INTERFACE_MODE, 0x00),
> @@ -175,7 +175,7 @@ static const struct qmp_phy_init_tbl msm8996_ufs_rx_tbl[] = {
> QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E),
> };
>
> -static const struct qmp_phy_init_tbl sm6115_ufsphy_serdes_tbl[] = {
> +static const struct qmp_phy_init_tbl sm6115_ufsphy_serdes[] = {
> QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x0e),
> QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14),
> QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
> @@ -231,12 +231,12 @@ static const struct qmp_phy_init_tbl sm6115_ufsphy_serdes_tbl[] = {
> QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x44),
> };
>
> -static const struct qmp_phy_init_tbl sm6115_ufsphy_tx_tbl[] = {
> +static const struct qmp_phy_init_tbl sm6115_ufsphy_tx[] = {
> QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
> QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
> };
>
> -static const struct qmp_phy_init_tbl sm6115_ufsphy_rx_tbl[] = {
> +static const struct qmp_phy_init_tbl sm6115_ufsphy_rx[] = {
> QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x24),
> QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x0F),
> QMP_PHY_INIT_CFG(QSERDES_RX_RX_INTERFACE_MODE, 0x40),
> @@ -254,7 +254,7 @@ static const struct qmp_phy_init_tbl sm6115_ufsphy_rx_tbl[] = {
> QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x5B),
> };
>
> -static const struct qmp_phy_init_tbl sm6115_ufsphy_pcs_tbl[] = {
> +static const struct qmp_phy_init_tbl sm6115_ufsphy_pcs[] = {
> QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_PWM_GEAR_BAND, 0x15),
> QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_SIGDET_CTRL2, 0x6d),
> QMP_PHY_INIT_CFG(QPHY_V2_PCS_TX_LARGE_AMP_DRV_LVL, 0x0f),
> @@ -266,7 +266,7 @@ static const struct qmp_phy_init_tbl sm6115_ufsphy_pcs_tbl[] = {
> QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_MIN_HIBERN8_TIME, 0x9a), /* 8 us */
> };
>
> -static const struct qmp_phy_init_tbl sdm845_ufsphy_serdes_tbl[] = {
> +static const struct qmp_phy_init_tbl sdm845_ufsphy_serdes[] = {
> QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
> QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04),
> QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a),
> @@ -308,13 +308,13 @@ static const struct qmp_phy_init_tbl sdm845_ufsphy_serdes_tbl[] = {
> QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x44),
> };
>
> -static const struct qmp_phy_init_tbl sdm845_ufsphy_tx_tbl[] = {
> +static const struct qmp_phy_init_tbl sdm845_ufsphy_tx[] = {
> QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06),
> QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x04),
> QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x07),
> };
>
> -static const struct qmp_phy_init_tbl sdm845_ufsphy_rx_tbl[] = {
> +static const struct qmp_phy_init_tbl sdm845_ufsphy_rx[] = {
> 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),
> @@ -333,7 +333,7 @@ static const struct qmp_phy_init_tbl sdm845_ufsphy_rx_tbl[] = {
> QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x59),
> };
>
> -static const struct qmp_phy_init_tbl sdm845_ufsphy_pcs_tbl[] = {
> +static const struct qmp_phy_init_tbl sdm845_ufsphy_pcs[] = {
> QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_RX_SIGDET_CTRL2, 0x6e),
> QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0a),
> QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_TX_SMALL_AMP_DRV_LVL, 0x02),
> @@ -344,7 +344,7 @@ static const struct qmp_phy_init_tbl sdm845_ufsphy_pcs_tbl[] = {
> QMP_PHY_INIT_CFG(QPHY_V3_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
> };
>
> -static const struct qmp_phy_init_tbl sm8150_ufsphy_serdes_tbl[] = {
> +static const struct qmp_phy_init_tbl sm8150_ufsphy_serdes[] = {
> QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0xd9),
> QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x11),
> QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL, 0x00),
> @@ -374,7 +374,7 @@ static const struct qmp_phy_init_tbl sm8150_ufsphy_serdes_tbl[] = {
> QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x06),
> };
>
> -static const struct qmp_phy_init_tbl sm8150_ufsphy_tx_tbl[] = {
> +static const struct qmp_phy_init_tbl sm8150_ufsphy_tx[] = {
> QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1, 0x06),
> QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1, 0x03),
> QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1, 0x01),
> @@ -383,7 +383,7 @@ static const struct qmp_phy_init_tbl sm8150_ufsphy_tx_tbl[] = {
> QMP_PHY_INIT_CFG(QSERDES_V4_TX_TRAN_DRVR_EMP_EN, 0x0c),
> };
>
> -static const struct qmp_phy_init_tbl sm8150_ufsphy_rx_tbl[] = {
> +static const struct qmp_phy_init_tbl sm8150_ufsphy_rx[] = {
> QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_LVL, 0x24),
> QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x0f),
> QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x1e),
> @@ -421,7 +421,7 @@ static const struct qmp_phy_init_tbl sm8150_ufsphy_rx_tbl[] = {
>
> };
>
> -static const struct qmp_phy_init_tbl sm8150_ufsphy_pcs_tbl[] = {
> +static const struct qmp_phy_init_tbl sm8150_ufsphy_pcs[] = {
> QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_RX_SIGDET_CTRL2, 0x6d),
> QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0a),
> QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_SMALL_AMP_DRV_LVL, 0x02),
> @@ -431,7 +431,7 @@ static const struct qmp_phy_init_tbl sm8150_ufsphy_pcs_tbl[] = {
> QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
> };
>
> -static const struct qmp_phy_init_tbl sm8350_ufsphy_serdes_tbl[] = {
> +static const struct qmp_phy_init_tbl sm8350_ufsphy_serdes[] = {
> QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0xd9),
> QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x11),
> QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_HS_SWITCH_SEL, 0x00),
> @@ -461,7 +461,7 @@ static const struct qmp_phy_init_tbl sm8350_ufsphy_serdes_tbl[] = {
> QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAP, 0x06),
> };
>
> -static const struct qmp_phy_init_tbl sm8350_ufsphy_tx_tbl[] = {
> +static const struct qmp_phy_init_tbl sm8350_ufsphy_tx[] = {
> QMP_PHY_INIT_CFG(QSERDES_V5_TX_PWM_GEAR_1_DIVIDER_BAND0_1, 0x06),
> QMP_PHY_INIT_CFG(QSERDES_V5_TX_PWM_GEAR_2_DIVIDER_BAND0_1, 0x03),
> QMP_PHY_INIT_CFG(QSERDES_V5_TX_PWM_GEAR_3_DIVIDER_BAND0_1, 0x01),
> @@ -473,7 +473,7 @@ static const struct qmp_phy_init_tbl sm8350_ufsphy_tx_tbl[] = {
> QMP_PHY_INIT_CFG(QSERDES_V5_TX_TRAN_DRVR_EMP_EN, 0x0c),
> };
>
> -static const struct qmp_phy_init_tbl sm8350_ufsphy_rx_tbl[] = {
> +static const struct qmp_phy_init_tbl sm8350_ufsphy_rx[] = {
> QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_LVL, 0x24),
> QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_CNTRL, 0x0f),
> QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_DEGLITCH_CNTRL, 0x1e),
> @@ -513,7 +513,7 @@ static const struct qmp_phy_init_tbl sm8350_ufsphy_rx_tbl[] = {
> QMP_PHY_INIT_CFG(QSERDES_V5_RX_DCC_CTRL1, 0x0c),
> };
>
> -static const struct qmp_phy_init_tbl sm8350_ufsphy_pcs_tbl[] = {
> +static const struct qmp_phy_init_tbl sm8350_ufsphy_pcs[] = {
> QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_RX_SIGDET_CTRL2, 0x6d),
> QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0a),
> QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_TX_SMALL_AMP_DRV_LVL, 0x02),
> @@ -531,19 +531,24 @@ static const struct qmp_phy_init_tbl sm8350_ufsphy_pcs_tbl[] = {
> QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
> };
>
> +struct qmp_phy_cfg_tables {
> + /* Init sequence for PHY blocks - serdes, tx, rx, pcs */
> + const struct qmp_phy_init_tbl *serdes;
> + int serdes_num;
> + const struct qmp_phy_init_tbl *tx;
> + int tx_num;
> + const struct qmp_phy_init_tbl *rx;
> + int rx_num;
> + const struct qmp_phy_init_tbl *pcs;
> + int pcs_num;
> +};
> +
> /* struct qmp_phy_cfg - per-PHY initialization config */
> struct qmp_phy_cfg {
> int lanes;
>
> - /* Init sequence for PHY blocks - serdes, tx, rx, pcs */
> - const struct qmp_phy_init_tbl *serdes_tbl;
> - int serdes_tbl_num;
> - const struct qmp_phy_init_tbl *tx_tbl;
> - int tx_tbl_num;
> - const struct qmp_phy_init_tbl *rx_tbl;
> - int rx_tbl_num;
> - const struct qmp_phy_init_tbl *pcs_tbl;
> - int pcs_tbl_num;
> + /* Main init sequence for PHY blocks - serdes, tx, rx, pcs */
> + const struct qmp_phy_cfg_tables tables;
>
> /* clock ids to be requested */
> const char * const *clk_list;
> @@ -660,12 +665,14 @@ static const char * const qmp_phy_vreg_l[] = {
> static const struct qmp_phy_cfg msm8996_ufs_cfg = {
> .lanes = 1,
>
> - .serdes_tbl = msm8996_ufs_serdes_tbl,
> - .serdes_tbl_num = ARRAY_SIZE(msm8996_ufs_serdes_tbl),
> - .tx_tbl = msm8996_ufs_tx_tbl,
> - .tx_tbl_num = ARRAY_SIZE(msm8996_ufs_tx_tbl),
> - .rx_tbl = msm8996_ufs_rx_tbl,
> - .rx_tbl_num = ARRAY_SIZE(msm8996_ufs_rx_tbl),
> + .tables = {
> + .serdes = msm8996_ufs_serdes,
> + .serdes_num = ARRAY_SIZE(msm8996_ufs_serdes),
> + .tx = msm8996_ufs_tx,
> + .tx_num = ARRAY_SIZE(msm8996_ufs_tx),
> + .rx = msm8996_ufs_rx,
> + .rx_num = ARRAY_SIZE(msm8996_ufs_rx),
> + },
>
> .clk_list = msm8996_ufs_phy_clk_l,
> .num_clks = ARRAY_SIZE(msm8996_ufs_phy_clk_l),
> @@ -685,14 +692,16 @@ static const struct qmp_phy_cfg msm8996_ufs_cfg = {
> static const struct qmp_phy_cfg sdm845_ufsphy_cfg = {
> .lanes = 2,
>
> - .serdes_tbl = sdm845_ufsphy_serdes_tbl,
> - .serdes_tbl_num = ARRAY_SIZE(sdm845_ufsphy_serdes_tbl),
> - .tx_tbl = sdm845_ufsphy_tx_tbl,
> - .tx_tbl_num = ARRAY_SIZE(sdm845_ufsphy_tx_tbl),
> - .rx_tbl = sdm845_ufsphy_rx_tbl,
> - .rx_tbl_num = ARRAY_SIZE(sdm845_ufsphy_rx_tbl),
> - .pcs_tbl = sdm845_ufsphy_pcs_tbl,
> - .pcs_tbl_num = ARRAY_SIZE(sdm845_ufsphy_pcs_tbl),
> + .tables = {
> + .serdes = sdm845_ufsphy_serdes,
> + .serdes_num = ARRAY_SIZE(sdm845_ufsphy_serdes),
> + .tx = sdm845_ufsphy_tx,
> + .tx_num = ARRAY_SIZE(sdm845_ufsphy_tx),
> + .rx = sdm845_ufsphy_rx,
> + .rx_num = ARRAY_SIZE(sdm845_ufsphy_rx),
> + .pcs = sdm845_ufsphy_pcs,
> + .pcs_num = ARRAY_SIZE(sdm845_ufsphy_pcs),
> + },
> .clk_list = sdm845_ufs_phy_clk_l,
> .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
> .vreg_list = qmp_phy_vreg_l,
> @@ -709,14 +718,16 @@ static const struct qmp_phy_cfg sdm845_ufsphy_cfg = {
> static const struct qmp_phy_cfg sm6115_ufsphy_cfg = {
> .lanes = 1,
>
> - .serdes_tbl = sm6115_ufsphy_serdes_tbl,
> - .serdes_tbl_num = ARRAY_SIZE(sm6115_ufsphy_serdes_tbl),
> - .tx_tbl = sm6115_ufsphy_tx_tbl,
> - .tx_tbl_num = ARRAY_SIZE(sm6115_ufsphy_tx_tbl),
> - .rx_tbl = sm6115_ufsphy_rx_tbl,
> - .rx_tbl_num = ARRAY_SIZE(sm6115_ufsphy_rx_tbl),
> - .pcs_tbl = sm6115_ufsphy_pcs_tbl,
> - .pcs_tbl_num = ARRAY_SIZE(sm6115_ufsphy_pcs_tbl),
> + .tables = {
> + .serdes = sm6115_ufsphy_serdes,
> + .serdes_num = ARRAY_SIZE(sm6115_ufsphy_serdes),
> + .tx = sm6115_ufsphy_tx,
> + .tx_num = ARRAY_SIZE(sm6115_ufsphy_tx),
> + .rx = sm6115_ufsphy_rx,
> + .rx_num = ARRAY_SIZE(sm6115_ufsphy_rx),
> + .pcs = sm6115_ufsphy_pcs,
> + .pcs_num = ARRAY_SIZE(sm6115_ufsphy_pcs),
> + },
> .clk_list = sdm845_ufs_phy_clk_l,
> .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
> .vreg_list = qmp_phy_vreg_l,
> @@ -732,14 +743,16 @@ static const struct qmp_phy_cfg sm6115_ufsphy_cfg = {
> static const struct qmp_phy_cfg sm8150_ufsphy_cfg = {
> .lanes = 2,
>
> - .serdes_tbl = sm8150_ufsphy_serdes_tbl,
> - .serdes_tbl_num = ARRAY_SIZE(sm8150_ufsphy_serdes_tbl),
> - .tx_tbl = sm8150_ufsphy_tx_tbl,
> - .tx_tbl_num = ARRAY_SIZE(sm8150_ufsphy_tx_tbl),
> - .rx_tbl = sm8150_ufsphy_rx_tbl,
> - .rx_tbl_num = ARRAY_SIZE(sm8150_ufsphy_rx_tbl),
> - .pcs_tbl = sm8150_ufsphy_pcs_tbl,
> - .pcs_tbl_num = ARRAY_SIZE(sm8150_ufsphy_pcs_tbl),
> + .tables = {
> + .serdes = sm8150_ufsphy_serdes,
> + .serdes_num = ARRAY_SIZE(sm8150_ufsphy_serdes),
> + .tx = sm8150_ufsphy_tx,
> + .tx_num = ARRAY_SIZE(sm8150_ufsphy_tx),
> + .rx = sm8150_ufsphy_rx,
> + .rx_num = ARRAY_SIZE(sm8150_ufsphy_rx),
> + .pcs = sm8150_ufsphy_pcs,
> + .pcs_num = ARRAY_SIZE(sm8150_ufsphy_pcs),
> + },
> .clk_list = sdm845_ufs_phy_clk_l,
> .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
> .vreg_list = qmp_phy_vreg_l,
> @@ -754,14 +767,16 @@ static const struct qmp_phy_cfg sm8150_ufsphy_cfg = {
> static const struct qmp_phy_cfg sm8350_ufsphy_cfg = {
> .lanes = 2,
>
> - .serdes_tbl = sm8350_ufsphy_serdes_tbl,
> - .serdes_tbl_num = ARRAY_SIZE(sm8350_ufsphy_serdes_tbl),
> - .tx_tbl = sm8350_ufsphy_tx_tbl,
> - .tx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_tx_tbl),
> - .rx_tbl = sm8350_ufsphy_rx_tbl,
> - .rx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_rx_tbl),
> - .pcs_tbl = sm8350_ufsphy_pcs_tbl,
> - .pcs_tbl_num = ARRAY_SIZE(sm8350_ufsphy_pcs_tbl),
> + .tables = {
> + .serdes = sm8350_ufsphy_serdes,
> + .serdes_num = ARRAY_SIZE(sm8350_ufsphy_serdes),
> + .tx = sm8350_ufsphy_tx,
> + .tx_num = ARRAY_SIZE(sm8350_ufsphy_tx),
> + .rx = sm8350_ufsphy_rx,
> + .rx_num = ARRAY_SIZE(sm8350_ufsphy_rx),
> + .pcs = sm8350_ufsphy_pcs,
> + .pcs_num = ARRAY_SIZE(sm8350_ufsphy_pcs),
> + },
> .clk_list = sdm845_ufs_phy_clk_l,
> .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
> .vreg_list = qmp_phy_vreg_l,
> @@ -776,14 +791,16 @@ static const struct qmp_phy_cfg sm8350_ufsphy_cfg = {
> static const struct qmp_phy_cfg sm8450_ufsphy_cfg = {
> .lanes = 2,
>
> - .serdes_tbl = sm8350_ufsphy_serdes_tbl,
> - .serdes_tbl_num = ARRAY_SIZE(sm8350_ufsphy_serdes_tbl),
> - .tx_tbl = sm8350_ufsphy_tx_tbl,
> - .tx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_tx_tbl),
> - .rx_tbl = sm8350_ufsphy_rx_tbl,
> - .rx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_rx_tbl),
> - .pcs_tbl = sm8350_ufsphy_pcs_tbl,
> - .pcs_tbl_num = ARRAY_SIZE(sm8350_ufsphy_pcs_tbl),
> + .tables = {
> + .serdes = sm8350_ufsphy_serdes,
> + .serdes_num = ARRAY_SIZE(sm8350_ufsphy_serdes),
> + .tx = sm8350_ufsphy_tx,
> + .tx_num = ARRAY_SIZE(sm8350_ufsphy_tx),
> + .rx = sm8350_ufsphy_rx,
> + .rx_num = ARRAY_SIZE(sm8350_ufsphy_rx),
> + .pcs = sm8350_ufsphy_pcs,
> + .pcs_num = ARRAY_SIZE(sm8350_ufsphy_pcs),
> + },
> .clk_list = sm8450_ufs_phy_clk_l,
> .num_clks = ARRAY_SIZE(sm8450_ufs_phy_clk_l),
> .vreg_list = qmp_phy_vreg_l,
> @@ -826,16 +843,43 @@ static void qmp_ufs_configure(void __iomem *base,
> qmp_ufs_configure_lane(base, regs, tbl, num, 0xff);
> }
>
> -static int qmp_ufs_serdes_init(struct qmp_phy *qphy)
> +static void qmp_ufs_serdes_init(struct qmp_phy *qphy, const struct qmp_phy_cfg_tables *tables)
> {
> const struct qmp_phy_cfg *cfg = qphy->cfg;
> void __iomem *serdes = qphy->serdes;
> - const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl;
> - int serdes_tbl_num = cfg->serdes_tbl_num;
>
> - qmp_ufs_configure(serdes, cfg->regs, serdes_tbl, serdes_tbl_num);
> + if (!tables)
> + return;
tables or tbls, take your pick, use one please not both
>
> - return 0;
> + qmp_ufs_configure(serdes, cfg->regs, tables->serdes, tables->serdes_num);
> +}
> +
> +static void qmp_ufs_lanes_init(struct qmp_phy *qphy, const struct qmp_phy_cfg_tables *tables)
> +{
> + const struct qmp_phy_cfg *cfg = qphy->cfg;
> + void __iomem *tx = qphy->tx;
> + void __iomem *rx = qphy->rx;
> +
> + qmp_ufs_configure_lane(tx, cfg->regs, tables->tx, tables->tx_num, 1);
> +
> + if (cfg->lanes >= 2)
> + qmp_ufs_configure_lane(qphy->tx2, cfg->regs, tables->tx, tables->tx_num, 2);
> +
> + qmp_ufs_configure_lane(rx, cfg->regs, tables->rx, tables->rx_num, 1);
> +
> + if (cfg->lanes >= 2)
> + qmp_ufs_configure_lane(qphy->rx2, cfg->regs, tables->rx, tables->rx_num, 2);
> +}
> +
> +static void qmp_ufs_pcs_init(struct qmp_phy *qphy, const struct qmp_phy_cfg_tables *tables)
> +{
> + const struct qmp_phy_cfg *cfg = qphy->cfg;
> + void __iomem *pcs = qphy->pcs;
> +
> + if (!tables)
> + return;
> +
> + qmp_ufs_configure(pcs, cfg->regs, tables->pcs, tables->pcs_num);
> }
>
> static int qmp_ufs_com_init(struct qmp_phy *qphy)
> @@ -933,31 +977,16 @@ static int qmp_ufs_power_on(struct phy *phy)
> struct qmp_phy *qphy = phy_get_drvdata(phy);
> struct qcom_qmp *qmp = qphy->qmp;
> const struct qmp_phy_cfg *cfg = qphy->cfg;
> - void __iomem *tx = qphy->tx;
> - void __iomem *rx = qphy->rx;
> void __iomem *pcs = qphy->pcs;
> void __iomem *status;
> unsigned int mask, val, ready;
> int ret;
>
> - qmp_ufs_serdes_init(qphy);
> -
> - /* Tx, Rx, and PCS configurations */
> - qmp_ufs_configure_lane(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num, 1);
> + qmp_ufs_serdes_init(qphy, &cfg->tables);
>
> - if (cfg->lanes >= 2) {
> - qmp_ufs_configure_lane(qphy->tx2, cfg->regs,
> - cfg->tx_tbl, cfg->tx_tbl_num, 2);
> - }
> -
> - qmp_ufs_configure_lane(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num, 1);
> -
> - if (cfg->lanes >= 2) {
> - qmp_ufs_configure_lane(qphy->rx2, cfg->regs,
> - cfg->rx_tbl, cfg->rx_tbl_num, 2);
> - }
> + qmp_ufs_lanes_init(qphy, &cfg->tables);
>
> - qmp_ufs_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
> + qmp_ufs_pcs_init(qphy, &cfg->tables);
>
> ret = reset_control_deassert(qmp->ufs_reset);
> if (ret)
> --
> 2.25.1
--
~Vinod
On 31-10-22, 23:32, Manivannan Sadhasivam wrote:
> Add separate tables_hs_b instance to allow the PHY driver to configure the
> PHY in HS Series B mode. The individual SoC configs need to supply the
> serdes register setting in tables_hs_b and the UFS driver can request the
> Series B mode by calling phy_set_mode() with mode set to PHY_MODE_UFS_HS_B.
>
> Reviewed-by: Dmitry Baryshkov <[email protected]>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>
> ---
> drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 18 ++++++++++++++++++
> 1 file changed, 18 insertions(+)
>
> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
> index cdfda4e6d575..4c6a2b5afc9a 100644
> --- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
> @@ -20,6 +20,8 @@
> #include <linux/reset.h>
> #include <linux/slab.h>
>
> +#include <ufs/unipro.h>
> +
> #include <dt-bindings/phy/phy.h>
>
> #include "phy-qcom-qmp.h"
> @@ -549,6 +551,8 @@ struct qmp_phy_cfg {
>
> /* Main init sequence for PHY blocks - serdes, tx, rx, pcs */
> const struct qmp_phy_cfg_tables tables;
> + /* Additional sequence for HS Series B */
> + const struct qmp_phy_cfg_tables tables_hs_b;
what am i missing, where was tables_hs_b added?
>
> /* clock ids to be requested */
> const char * const *clk_list;
> @@ -582,6 +586,7 @@ struct qmp_phy_cfg {
> * @rx2: iomapped memory space for second lane's rx (in dual lane PHYs)
> * @pcs_misc: iomapped memory space for lane's pcs_misc
> * @qmp: QMP phy to which this lane belongs
> + * @mode: PHY mode configured by the UFS driver
> */
> struct qmp_phy {
> struct phy *phy;
> @@ -594,6 +599,7 @@ struct qmp_phy {
> void __iomem *rx2;
> void __iomem *pcs_misc;
> struct qcom_qmp *qmp;
> + u32 mode;
> };
>
> /**
> @@ -983,6 +989,8 @@ static int qmp_ufs_power_on(struct phy *phy)
> int ret;
>
> qmp_ufs_serdes_init(qphy, &cfg->tables);
> + if (qphy->mode == PHY_MODE_UFS_HS_B)
> + qmp_ufs_serdes_init(qphy, &cfg->tables_hs_b);
>
> qmp_ufs_lanes_init(qphy, &cfg->tables);
>
> @@ -1070,6 +1078,15 @@ static int qmp_ufs_disable(struct phy *phy)
> return qmp_ufs_exit(phy);
> }
>
> +static int qmp_ufs_set_mode(struct phy *phy, enum phy_mode mode, int submode)
> +{
> + struct qmp_phy *qphy = phy_get_drvdata(phy);
> +
> + qphy->mode = mode;
> +
> + return 0;
> +}
> +
> static int qmp_ufs_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg)
> {
> struct qcom_qmp *qmp = dev_get_drvdata(dev);
> @@ -1105,6 +1122,7 @@ static int qmp_ufs_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg)
> static const struct phy_ops qcom_qmp_ufs_ops = {
> .power_on = qmp_ufs_enable,
> .power_off = qmp_ufs_disable,
> + .set_mode = qmp_ufs_set_mode,
> .owner = THIS_MODULE,
> };
>
> --
> 2.25.1
--
~Vinod
On Thu, Nov 03, 2022 at 11:23:17AM -0400, Krzysztof Kozlowski wrote:
> On 03/11/2022 08:28, Manivannan Sadhasivam wrote:
> > On Wed, Nov 02, 2022 at 03:09:50PM -0400, Krzysztof Kozlowski wrote:
> >> On 31/10/2022 14:02, Manivannan Sadhasivam wrote:
> >>> The maximum gear supported by the UFS device can be specified using the
> >>> "max-device-gear" property. This allows the UFS controller to configure the
> >>> TX/RX gear before starting communication with the UFS device.
> >>
> >> This is confusing. The UFS PHY provides gear capability, so what is the
> >> "device" here? The attached memory? How could it report something else
> >> than phy?
> >>
> >
> > This is the norm with any storage protocol, right? Both host and device
> > (memory) can support different speeds and the OEM can choose to put any
> > combinations (even though it might not be very efficient).
> >
> > For instance,
> >
> > PHY (G4) -> Device (G3)
>
> Yes and look at MMC - no need to define "max mode" supported by eMMC.
> You define the modes supported by controller but the memory capabilities
> are being autodetected and negotiated.
>
> >
> > From the host perspective we know what the PHY can support but that's not the
> > same with the device until probing it. And probing requires using a minimum
> > supported gear. For sure we can use something like G2/G3 and reinit later but
> > as I learnt, that approach was rejected by the community when submitted
> > by Qualcomm earlier.
>
> It should be then referenced somewhere as it might be a reason to accept
> the property.
>
> >
> >> The last sentence also suggests that you statically encode gear to avoid
> >> runtime negotiation.
> >>
> >
> > Yes, the OEM should know what the max gear speed they want to run, so getting
> > this info from DT makes sense.
>
> Not really if it is auto-detectable. Just because things are static is
> not the sole reason to put them into DT. The reason is - they are not
> detectable by OS/firmware thus we must have them in DT to be able to
> know it.
>
Since I'm not able to get a link to the previous discussion, I'm gonna
implement the reinit support and post the next iteration. Let's see how it
turns up.
Thanks,
Mani
>
>
> Best regards,
> Krzysztof
>
--
மணிவண்ணன் சதாசிவம்
On Fri, Nov 11, 2022 at 12:31:15PM +0530, Vinod Koul wrote:
> On 31-10-22, 23:32, Manivannan Sadhasivam wrote:
> > Add separate tables_hs_b instance to allow the PHY driver to configure the
> > PHY in HS Series B mode. The individual SoC configs need to supply the
> > serdes register setting in tables_hs_b and the UFS driver can request the
> > Series B mode by calling phy_set_mode() with mode set to PHY_MODE_UFS_HS_B.
> >
> > Reviewed-by: Dmitry Baryshkov <[email protected]>
> > Signed-off-by: Manivannan Sadhasivam <[email protected]>
> > ---
> > drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 18 ++++++++++++++++++
> > 1 file changed, 18 insertions(+)
> >
> > diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
> > index cdfda4e6d575..4c6a2b5afc9a 100644
> > --- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
> > +++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
> > @@ -20,6 +20,8 @@
> > #include <linux/reset.h>
> > #include <linux/slab.h>
> >
> > +#include <ufs/unipro.h>
> > +
> > #include <dt-bindings/phy/phy.h>
> >
> > #include "phy-qcom-qmp.h"
> > @@ -549,6 +551,8 @@ struct qmp_phy_cfg {
> >
> > /* Main init sequence for PHY blocks - serdes, tx, rx, pcs */
> > const struct qmp_phy_cfg_tables tables;
> > + /* Additional sequence for HS Series B */
> > + const struct qmp_phy_cfg_tables tables_hs_b;
>
> what am i missing, where was tables_hs_b added?
>
This patch adds the infrastructure for HS_B mode. The actual table is added in
patch 5/15.
Thanks,
Mani
> >
> > /* clock ids to be requested */
> > const char * const *clk_list;
> > @@ -582,6 +586,7 @@ struct qmp_phy_cfg {
> > * @rx2: iomapped memory space for second lane's rx (in dual lane PHYs)
> > * @pcs_misc: iomapped memory space for lane's pcs_misc
> > * @qmp: QMP phy to which this lane belongs
> > + * @mode: PHY mode configured by the UFS driver
> > */
> > struct qmp_phy {
> > struct phy *phy;
> > @@ -594,6 +599,7 @@ struct qmp_phy {
> > void __iomem *rx2;
> > void __iomem *pcs_misc;
> > struct qcom_qmp *qmp;
> > + u32 mode;
> > };
> >
> > /**
> > @@ -983,6 +989,8 @@ static int qmp_ufs_power_on(struct phy *phy)
> > int ret;
> >
> > qmp_ufs_serdes_init(qphy, &cfg->tables);
> > + if (qphy->mode == PHY_MODE_UFS_HS_B)
> > + qmp_ufs_serdes_init(qphy, &cfg->tables_hs_b);
> >
> > qmp_ufs_lanes_init(qphy, &cfg->tables);
> >
> > @@ -1070,6 +1078,15 @@ static int qmp_ufs_disable(struct phy *phy)
> > return qmp_ufs_exit(phy);
> > }
> >
> > +static int qmp_ufs_set_mode(struct phy *phy, enum phy_mode mode, int submode)
> > +{
> > + struct qmp_phy *qphy = phy_get_drvdata(phy);
> > +
> > + qphy->mode = mode;
> > +
> > + return 0;
> > +}
> > +
> > static int qmp_ufs_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg)
> > {
> > struct qcom_qmp *qmp = dev_get_drvdata(dev);
> > @@ -1105,6 +1122,7 @@ static int qmp_ufs_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg)
> > static const struct phy_ops qcom_qmp_ufs_ops = {
> > .power_on = qmp_ufs_enable,
> > .power_off = qmp_ufs_disable,
> > + .set_mode = qmp_ufs_set_mode,
> > .owner = THIS_MODULE,
> > };
> >
> > --
> > 2.25.1
>
> --
> ~Vinod
--
மணிவண்ணன் சதாசிவம்