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
)
69 static int ufs_qcom_phy_qmp_20nm_exit(struct phy
*generic_phy
)
75 void ufs_qcom_phy_qmp_20nm_power_control(struct ufs_qcom_phy
*phy
, bool val
)
77 bool hibern8_exit_after_pwr_collapse
= phy
->quirks
&
78 UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE
;
81 writel_relaxed(0x1, phy
->mmio
+ UFS_PHY_POWER_DOWN_CONTROL
);
83 * Before any transactions involving PHY, ensure PHY knows
84 * that it's analog rail is powered ON.
88 if (hibern8_exit_after_pwr_collapse
) {
90 * Give atleast 1us delay after restoring PHY analog
94 writel_relaxed(0x0A, phy
->mmio
+
95 QSERDES_COM_SYSCLK_EN_SEL_TXBAND
);
96 writel_relaxed(0x08, phy
->mmio
+
97 QSERDES_COM_SYSCLK_EN_SEL_TXBAND
);
99 * Make sure workaround is deactivated before proceeding
100 * with normal PHY operations.
105 if (hibern8_exit_after_pwr_collapse
) {
106 writel_relaxed(0x0A, phy
->mmio
+
107 QSERDES_COM_SYSCLK_EN_SEL_TXBAND
);
108 writel_relaxed(0x02, phy
->mmio
+
109 QSERDES_COM_SYSCLK_EN_SEL_TXBAND
);
111 * Make sure that above workaround is activated before
112 * PHY analog power collapse.
117 writel_relaxed(0x0, phy
->mmio
+ UFS_PHY_POWER_DOWN_CONTROL
);
119 * ensure that PHY knows its PHY analog rail is going
127 void ufs_qcom_phy_qmp_20nm_set_tx_lane_enable(struct ufs_qcom_phy
*phy
, u32 val
)
129 writel_relaxed(val
& UFS_PHY_TX_LANE_ENABLE_MASK
,
130 phy
->mmio
+ UFS_PHY_TX_LANE_ENABLE
);
134 static inline void ufs_qcom_phy_qmp_20nm_start_serdes(struct ufs_qcom_phy
*phy
)
138 tmp
= readl_relaxed(phy
->mmio
+ UFS_PHY_PHY_START
);
139 tmp
&= ~MASK_SERDES_START
;
140 tmp
|= (1 << OFFSET_SERDES_START
);
141 writel_relaxed(tmp
, phy
->mmio
+ UFS_PHY_PHY_START
);
145 static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy
*phy_common
)
150 err
= readl_poll_timeout(phy_common
->mmio
+ UFS_PHY_PCS_READY_STATUS
,
151 val
, (val
& MASK_PCS_READY
), 10, 1000000);
153 dev_err(phy_common
->dev
, "%s: poll for pcs failed err = %d\n",
158 static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops
= {
159 .init
= ufs_qcom_phy_qmp_20nm_init
,
160 .exit
= ufs_qcom_phy_qmp_20nm_exit
,
161 .power_on
= ufs_qcom_phy_power_on
,
162 .power_off
= ufs_qcom_phy_power_off
,
163 .owner
= THIS_MODULE
,
166 static struct ufs_qcom_phy_specific_ops phy_20nm_ops
= {
167 .calibrate_phy
= ufs_qcom_phy_qmp_20nm_phy_calibrate
,
168 .start_serdes
= ufs_qcom_phy_qmp_20nm_start_serdes
,
169 .is_physical_coding_sublayer_ready
= ufs_qcom_phy_qmp_20nm_is_pcs_ready
,
170 .set_tx_lane_enable
= ufs_qcom_phy_qmp_20nm_set_tx_lane_enable
,
171 .power_control
= ufs_qcom_phy_qmp_20nm_power_control
,
174 static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device
*pdev
)
176 struct device
*dev
= &pdev
->dev
;
177 struct phy
*generic_phy
;
178 struct ufs_qcom_phy_qmp_20nm
*phy
;
179 struct ufs_qcom_phy
*phy_common
;
182 phy
= devm_kzalloc(dev
, sizeof(*phy
), GFP_KERNEL
);
187 phy_common
= &phy
->common_cfg
;
189 generic_phy
= ufs_qcom_phy_generic_probe(pdev
, phy_common
,
190 &ufs_qcom_phy_qmp_20nm_phy_ops
, &phy_20nm_ops
);
193 dev_err(dev
, "%s: ufs_qcom_phy_generic_probe() failed\n",
199 err
= ufs_qcom_phy_init_clks(phy_common
);
201 dev_err(phy_common
->dev
, "%s: ufs_qcom_phy_init_clks() failed %d\n",
206 err
= ufs_qcom_phy_init_vregulators(phy_common
);
208 dev_err(phy_common
->dev
, "%s: ufs_qcom_phy_init_vregulators() failed %d\n",
213 ufs_qcom_phy_qmp_20nm_advertise_quirks(phy_common
);
215 phy_set_drvdata(generic_phy
, phy
);
217 strlcpy(phy_common
->name
, UFS_PHY_NAME
, sizeof(phy_common
->name
));
223 static const struct of_device_id ufs_qcom_phy_qmp_20nm_of_match
[] = {
224 {.compatible
= "qcom,ufs-phy-qmp-20nm"},
227 MODULE_DEVICE_TABLE(of
, ufs_qcom_phy_qmp_20nm_of_match
);
229 static struct platform_driver ufs_qcom_phy_qmp_20nm_driver
= {
230 .probe
= ufs_qcom_phy_qmp_20nm_probe
,
232 .of_match_table
= ufs_qcom_phy_qmp_20nm_of_match
,
233 .name
= "ufs_qcom_phy_qmp_20nm",
237 module_platform_driver(ufs_qcom_phy_qmp_20nm_driver
);
239 MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 20nm");
240 MODULE_LICENSE("GPL v2");