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_qmp_20nm
*phy
= phy_get_drvdata(generic_phy
);
67 struct ufs_qcom_phy
*phy_common
= &phy
->common_cfg
;
70 err
= ufs_qcom_phy_init_clks(generic_phy
, phy_common
);
72 dev_err(phy_common
->dev
, "%s: ufs_qcom_phy_init_clks() failed %d\n",
77 err
= ufs_qcom_phy_init_vregulators(generic_phy
, phy_common
);
79 dev_err(phy_common
->dev
, "%s: ufs_qcom_phy_init_vregulators() failed %d\n",
84 ufs_qcom_phy_qmp_20nm_advertise_quirks(phy_common
);
91 void ufs_qcom_phy_qmp_20nm_power_control(struct ufs_qcom_phy
*phy
, bool val
)
93 bool hibern8_exit_after_pwr_collapse
= phy
->quirks
&
94 UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE
;
97 writel_relaxed(0x1, phy
->mmio
+ UFS_PHY_POWER_DOWN_CONTROL
);
99 * Before any transactions involving PHY, ensure PHY knows
100 * that it's analog rail is powered ON.
104 if (hibern8_exit_after_pwr_collapse
) {
106 * Give atleast 1us delay after restoring PHY analog
110 writel_relaxed(0x0A, phy
->mmio
+
111 QSERDES_COM_SYSCLK_EN_SEL_TXBAND
);
112 writel_relaxed(0x08, phy
->mmio
+
113 QSERDES_COM_SYSCLK_EN_SEL_TXBAND
);
115 * Make sure workaround is deactivated before proceeding
116 * with normal PHY operations.
121 if (hibern8_exit_after_pwr_collapse
) {
122 writel_relaxed(0x0A, phy
->mmio
+
123 QSERDES_COM_SYSCLK_EN_SEL_TXBAND
);
124 writel_relaxed(0x02, phy
->mmio
+
125 QSERDES_COM_SYSCLK_EN_SEL_TXBAND
);
127 * Make sure that above workaround is activated before
128 * PHY analog power collapse.
133 writel_relaxed(0x0, phy
->mmio
+ UFS_PHY_POWER_DOWN_CONTROL
);
135 * ensure that PHY knows its PHY analog rail is going
143 void ufs_qcom_phy_qmp_20nm_set_tx_lane_enable(struct ufs_qcom_phy
*phy
, u32 val
)
145 writel_relaxed(val
& UFS_PHY_TX_LANE_ENABLE_MASK
,
146 phy
->mmio
+ UFS_PHY_TX_LANE_ENABLE
);
150 static inline void ufs_qcom_phy_qmp_20nm_start_serdes(struct ufs_qcom_phy
*phy
)
154 tmp
= readl_relaxed(phy
->mmio
+ UFS_PHY_PHY_START
);
155 tmp
&= ~MASK_SERDES_START
;
156 tmp
|= (1 << OFFSET_SERDES_START
);
157 writel_relaxed(tmp
, phy
->mmio
+ UFS_PHY_PHY_START
);
161 static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy
*phy_common
)
166 err
= readl_poll_timeout(phy_common
->mmio
+ UFS_PHY_PCS_READY_STATUS
,
167 val
, (val
& MASK_PCS_READY
), 10, 1000000);
169 dev_err(phy_common
->dev
, "%s: poll for pcs failed err = %d\n",
174 static struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops
= {
175 .init
= ufs_qcom_phy_qmp_20nm_init
,
176 .exit
= ufs_qcom_phy_exit
,
177 .power_on
= ufs_qcom_phy_power_on
,
178 .power_off
= ufs_qcom_phy_power_off
,
179 .owner
= THIS_MODULE
,
182 static struct ufs_qcom_phy_specific_ops phy_20nm_ops
= {
183 .calibrate_phy
= ufs_qcom_phy_qmp_20nm_phy_calibrate
,
184 .start_serdes
= ufs_qcom_phy_qmp_20nm_start_serdes
,
185 .is_physical_coding_sublayer_ready
= ufs_qcom_phy_qmp_20nm_is_pcs_ready
,
186 .set_tx_lane_enable
= ufs_qcom_phy_qmp_20nm_set_tx_lane_enable
,
187 .power_control
= ufs_qcom_phy_qmp_20nm_power_control
,
190 static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device
*pdev
)
192 struct device
*dev
= &pdev
->dev
;
193 struct phy
*generic_phy
;
194 struct ufs_qcom_phy_qmp_20nm
*phy
;
197 phy
= devm_kzalloc(dev
, sizeof(*phy
), GFP_KERNEL
);
199 dev_err(dev
, "%s: failed to allocate phy\n", __func__
);
204 generic_phy
= ufs_qcom_phy_generic_probe(pdev
, &phy
->common_cfg
,
205 &ufs_qcom_phy_qmp_20nm_phy_ops
, &phy_20nm_ops
);
208 dev_err(dev
, "%s: ufs_qcom_phy_generic_probe() failed\n",
214 phy_set_drvdata(generic_phy
, phy
);
216 strlcpy(phy
->common_cfg
.name
, UFS_PHY_NAME
,
217 sizeof(phy
->common_cfg
.name
));
223 static int ufs_qcom_phy_qmp_20nm_remove(struct platform_device
*pdev
)
225 struct device
*dev
= &pdev
->dev
;
226 struct phy
*generic_phy
= to_phy(dev
);
227 struct ufs_qcom_phy
*ufs_qcom_phy
= get_ufs_qcom_phy(generic_phy
);
230 err
= ufs_qcom_phy_remove(generic_phy
, ufs_qcom_phy
);
232 dev_err(dev
, "%s: ufs_qcom_phy_remove failed = %d\n",
238 static const struct of_device_id ufs_qcom_phy_qmp_20nm_of_match
[] = {
239 {.compatible
= "qcom,ufs-phy-qmp-20nm"},
242 MODULE_DEVICE_TABLE(of
, ufs_qcom_phy_qmp_20nm_of_match
);
244 static struct platform_driver ufs_qcom_phy_qmp_20nm_driver
= {
245 .probe
= ufs_qcom_phy_qmp_20nm_probe
,
246 .remove
= ufs_qcom_phy_qmp_20nm_remove
,
248 .of_match_table
= ufs_qcom_phy_qmp_20nm_of_match
,
249 .name
= "ufs_qcom_phy_qmp_20nm",
250 .owner
= THIS_MODULE
,
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");