2 * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
15 #include "phy-qcom-ufs-qmp-20nm.h"
17 #define UFS_PHY_NAME "ufs_phy_qmp_20nm"
20 int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy
*ufs_qcom_phy
,
23 struct ufs_qcom_phy_calibration
*tbl_A
, *tbl_B
;
24 int tbl_size_A
, tbl_size_B
;
25 u8 major
= ufs_qcom_phy
->host_ctrl_rev_major
;
26 u16 minor
= ufs_qcom_phy
->host_ctrl_rev_minor
;
27 u16 step
= ufs_qcom_phy
->host_ctrl_rev_step
;
30 if ((major
== 0x1) && (minor
== 0x002) && (step
== 0x0000)) {
31 tbl_size_A
= ARRAY_SIZE(phy_cal_table_rate_A_1_2_0
);
32 tbl_A
= phy_cal_table_rate_A_1_2_0
;
33 } else if ((major
== 0x1) && (minor
== 0x003) && (step
== 0x0000)) {
34 tbl_size_A
= ARRAY_SIZE(phy_cal_table_rate_A_1_3_0
);
35 tbl_A
= phy_cal_table_rate_A_1_3_0
;
37 dev_err(ufs_qcom_phy
->dev
, "%s: Unknown UFS-PHY version, no calibration values\n",
43 tbl_size_B
= ARRAY_SIZE(phy_cal_table_rate_B
);
44 tbl_B
= phy_cal_table_rate_B
;
46 err
= ufs_qcom_phy_calibrate(ufs_qcom_phy
, tbl_A
, tbl_size_A
,
47 tbl_B
, tbl_size_B
, is_rate_B
);
50 dev_err(ufs_qcom_phy
->dev
, "%s: ufs_qcom_phy_calibrate() failed %d\n",
58 void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy
*phy_common
)
61 UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE
;
64 static int ufs_qcom_phy_qmp_20nm_init(struct phy
*generic_phy
)
66 struct ufs_qcom_phy
*phy_common
= get_ufs_qcom_phy(generic_phy
);
67 bool is_rate_B
= false;
70 if (phy_common
->mode
== PHY_MODE_UFS_HS_B
)
73 ret
= ufs_qcom_phy_qmp_20nm_phy_calibrate(phy_common
, is_rate_B
);
75 /* phy calibrated, but yet to be started */
76 phy_common
->is_started
= false;
81 static int ufs_qcom_phy_qmp_20nm_exit(struct phy
*generic_phy
)
87 int ufs_qcom_phy_qmp_20nm_set_mode(struct phy
*generic_phy
, enum phy_mode mode
)
89 struct ufs_qcom_phy
*phy_common
= get_ufs_qcom_phy(generic_phy
);
91 phy_common
->mode
= PHY_MODE_INVALID
;
94 phy_common
->mode
= mode
;
100 void ufs_qcom_phy_qmp_20nm_power_control(struct ufs_qcom_phy
*phy
, bool val
)
102 bool hibern8_exit_after_pwr_collapse
= phy
->quirks
&
103 UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE
;
106 writel_relaxed(0x1, phy
->mmio
+ UFS_PHY_POWER_DOWN_CONTROL
);
108 * Before any transactions involving PHY, ensure PHY knows
109 * that it's analog rail is powered ON.
113 if (hibern8_exit_after_pwr_collapse
) {
115 * Give atleast 1us delay after restoring PHY analog
119 writel_relaxed(0x0A, phy
->mmio
+
120 QSERDES_COM_SYSCLK_EN_SEL_TXBAND
);
121 writel_relaxed(0x08, phy
->mmio
+
122 QSERDES_COM_SYSCLK_EN_SEL_TXBAND
);
124 * Make sure workaround is deactivated before proceeding
125 * with normal PHY operations.
130 if (hibern8_exit_after_pwr_collapse
) {
131 writel_relaxed(0x0A, phy
->mmio
+
132 QSERDES_COM_SYSCLK_EN_SEL_TXBAND
);
133 writel_relaxed(0x02, phy
->mmio
+
134 QSERDES_COM_SYSCLK_EN_SEL_TXBAND
);
136 * Make sure that above workaround is activated before
137 * PHY analog power collapse.
142 writel_relaxed(0x0, phy
->mmio
+ UFS_PHY_POWER_DOWN_CONTROL
);
144 * ensure that PHY knows its PHY analog rail is going
152 void ufs_qcom_phy_qmp_20nm_set_tx_lane_enable(struct ufs_qcom_phy
*phy
, u32 val
)
154 writel_relaxed(val
& UFS_PHY_TX_LANE_ENABLE_MASK
,
155 phy
->mmio
+ UFS_PHY_TX_LANE_ENABLE
);
159 static inline void ufs_qcom_phy_qmp_20nm_start_serdes(struct ufs_qcom_phy
*phy
)
163 tmp
= readl_relaxed(phy
->mmio
+ UFS_PHY_PHY_START
);
164 tmp
&= ~MASK_SERDES_START
;
165 tmp
|= (1 << OFFSET_SERDES_START
);
166 writel_relaxed(tmp
, phy
->mmio
+ UFS_PHY_PHY_START
);
170 static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy
*phy_common
)
175 err
= readl_poll_timeout(phy_common
->mmio
+ UFS_PHY_PCS_READY_STATUS
,
176 val
, (val
& MASK_PCS_READY
), 10, 1000000);
178 dev_err(phy_common
->dev
, "%s: poll for pcs failed err = %d\n",
183 static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops
= {
184 .init
= ufs_qcom_phy_qmp_20nm_init
,
185 .exit
= ufs_qcom_phy_qmp_20nm_exit
,
186 .power_on
= ufs_qcom_phy_power_on
,
187 .power_off
= ufs_qcom_phy_power_off
,
188 .set_mode
= ufs_qcom_phy_qmp_20nm_set_mode
,
189 .owner
= THIS_MODULE
,
192 static struct ufs_qcom_phy_specific_ops phy_20nm_ops
= {
193 .start_serdes
= ufs_qcom_phy_qmp_20nm_start_serdes
,
194 .is_physical_coding_sublayer_ready
= ufs_qcom_phy_qmp_20nm_is_pcs_ready
,
195 .set_tx_lane_enable
= ufs_qcom_phy_qmp_20nm_set_tx_lane_enable
,
196 .power_control
= ufs_qcom_phy_qmp_20nm_power_control
,
199 static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device
*pdev
)
201 struct device
*dev
= &pdev
->dev
;
202 struct phy
*generic_phy
;
203 struct ufs_qcom_phy_qmp_20nm
*phy
;
204 struct ufs_qcom_phy
*phy_common
;
207 phy
= devm_kzalloc(dev
, sizeof(*phy
), GFP_KERNEL
);
212 phy_common
= &phy
->common_cfg
;
214 generic_phy
= ufs_qcom_phy_generic_probe(pdev
, phy_common
,
215 &ufs_qcom_phy_qmp_20nm_phy_ops
, &phy_20nm_ops
);
222 err
= ufs_qcom_phy_init_clks(phy_common
);
226 err
= ufs_qcom_phy_init_vregulators(phy_common
);
230 ufs_qcom_phy_qmp_20nm_advertise_quirks(phy_common
);
232 phy_set_drvdata(generic_phy
, phy
);
234 strlcpy(phy_common
->name
, UFS_PHY_NAME
, sizeof(phy_common
->name
));
240 static const struct of_device_id ufs_qcom_phy_qmp_20nm_of_match
[] = {
241 {.compatible
= "qcom,ufs-phy-qmp-20nm"},
244 MODULE_DEVICE_TABLE(of
, ufs_qcom_phy_qmp_20nm_of_match
);
246 static struct platform_driver ufs_qcom_phy_qmp_20nm_driver
= {
247 .probe
= ufs_qcom_phy_qmp_20nm_probe
,
249 .of_match_table
= ufs_qcom_phy_qmp_20nm_of_match
,
250 .name
= "ufs_qcom_phy_qmp_20nm",
254 module_platform_driver(ufs_qcom_phy_qmp_20nm_driver
);
256 MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 20nm");
257 MODULE_LICENSE("GPL v2");