blob: 9450e18df435e69d5139596740692098414919cc [file] [log] [blame]
Subhash Jadavani9c807702017-04-01 00:35:51 -07001/*
2 * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
3 *
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.
7 *
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.
12 *
13 */
14
15#include "phy-qcom-ufs-qmp-v3-660.h"
16
17#define UFS_PHY_NAME "ufs_phy_qmp_v3_660"
18
19static
20int ufs_qcom_phy_qmp_v3_660_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
21 bool is_rate_B)
22{
23 int err;
24 int tbl_size_A, tbl_size_B;
25 struct ufs_qcom_phy_calibration *tbl_A, *tbl_B;
26 u8 major = ufs_qcom_phy->host_ctrl_rev_major;
27 u16 minor = ufs_qcom_phy->host_ctrl_rev_minor;
28 u16 step = ufs_qcom_phy->host_ctrl_rev_step;
29
30 tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
31 tbl_B = phy_cal_table_rate_B;
32
33 if ((major == 0x3) && (minor == 0x001) && (step == 0x001)) {
34 tbl_A = phy_cal_table_rate_A_3_1_1;
35 tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_3_1_1);
36 } else {
37 dev_err(ufs_qcom_phy->dev,
38 "%s: Unknown UFS-PHY version (major 0x%x minor 0x%x step 0x%x), no calibration values\n",
39 __func__, major, minor, step);
40 err = -ENODEV;
41 goto out;
42 }
43
44 err = ufs_qcom_phy_calibrate(ufs_qcom_phy,
45 tbl_A, tbl_size_A,
46 tbl_B, tbl_size_B,
47 is_rate_B);
48
49 if (err)
50 dev_err(ufs_qcom_phy->dev,
51 "%s: ufs_qcom_phy_calibrate() failed %d\n",
52 __func__, err);
53
54out:
55 return err;
56}
57
58static int ufs_qcom_phy_qmp_v3_660_init(struct phy *generic_phy)
59{
60 struct ufs_qcom_phy_qmp_v3_660 *phy = phy_get_drvdata(generic_phy);
61 struct ufs_qcom_phy *phy_common = &phy->common_cfg;
62 int err;
63
64 err = ufs_qcom_phy_init_clks(generic_phy, phy_common);
65 if (err) {
66 dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_clks() failed %d\n",
67 __func__, err);
68 goto out;
69 }
70
71 err = ufs_qcom_phy_init_vregulators(generic_phy, phy_common);
72 if (err) {
73 dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_vregulators() failed %d\n",
74 __func__, err);
75 goto out;
76 }
77
78out:
79 return err;
80}
81
82static
83void ufs_qcom_phy_qmp_v3_660_power_control(struct ufs_qcom_phy *phy,
84 bool power_ctrl)
85{
86 if (!power_ctrl) {
87 /* apply analog power collapse */
88 writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
89 /*
90 * Make sure that PHY knows its analog rail is going to be
91 * powered OFF.
92 */
93 mb();
94 } else {
95 /* bring PHY out of analog power collapse */
96 writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
97
98 /*
99 * Before any transactions involving PHY, ensure PHY knows
100 * that it's analog rail is powered ON.
101 */
102 mb();
103 }
104}
105
106static inline
107void ufs_qcom_phy_qmp_v3_660_set_tx_lane_enable(struct ufs_qcom_phy *phy,
108 u32 val)
109{
110 /*
111 * v3 PHY does not have TX_LANE_ENABLE register.
112 * Implement this function so as not to propagate error to caller.
113 */
114}
115
116static
117void ufs_qcom_phy_qmp_v3_660_ctrl_rx_linecfg(struct ufs_qcom_phy *phy,
118 bool ctrl)
119{
120 u32 temp;
121
122 temp = readl_relaxed(phy->mmio + UFS_PHY_LINECFG_DISABLE);
123
124 if (ctrl) /* enable RX LineCfg */
125 temp &= ~UFS_PHY_RX_LINECFG_DISABLE_BIT;
126 else /* disable RX LineCfg */
127 temp |= UFS_PHY_RX_LINECFG_DISABLE_BIT;
128
129 writel_relaxed(temp, phy->mmio + UFS_PHY_LINECFG_DISABLE);
130 /* Make sure that RX LineCfg config applied before we return */
131 mb();
132}
133
134static inline void ufs_qcom_phy_qmp_v3_660_start_serdes(
135 struct ufs_qcom_phy *phy)
136{
137 u32 tmp;
138
139 tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START);
140 tmp &= ~MASK_SERDES_START;
141 tmp |= (1 << OFFSET_SERDES_START);
142 writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START);
143 /* Ensure register value is committed */
144 mb();
145}
146
147static int ufs_qcom_phy_qmp_v3_660_is_pcs_ready(
148 struct ufs_qcom_phy *phy_common)
149{
150 int err = 0;
151 u32 val;
152
153 err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS,
154 val, (val & MASK_PCS_READY), 10, 1000000);
155 if (err)
156 dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n",
157 __func__, err);
158 return err;
159}
160
161static void ufs_qcom_phy_qmp_v3_660_dbg_register_dump(
162 struct ufs_qcom_phy *phy)
163{
164 ufs_qcom_phy_dump_regs(phy, COM_BASE, COM_SIZE,
165 "PHY QSERDES COM Registers ");
166 ufs_qcom_phy_dump_regs(phy, PHY_BASE, PHY_SIZE,
167 "PHY Registers ");
168 ufs_qcom_phy_dump_regs(phy, RX_BASE, RX_SIZE,
169 "PHY RX0 Registers ");
170 ufs_qcom_phy_dump_regs(phy, TX_BASE, TX_SIZE,
171 "PHY TX0 Registers ");
172}
173
174struct phy_ops ufs_qcom_phy_qmp_v3_660_phy_ops = {
175 .init = ufs_qcom_phy_qmp_v3_660_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,
180};
181
182struct ufs_qcom_phy_specific_ops phy_v3_660_ops = {
183 .calibrate_phy = ufs_qcom_phy_qmp_v3_660_phy_calibrate,
184 .start_serdes = ufs_qcom_phy_qmp_v3_660_start_serdes,
185 .is_physical_coding_sublayer_ready =
186 ufs_qcom_phy_qmp_v3_660_is_pcs_ready,
187 .set_tx_lane_enable = ufs_qcom_phy_qmp_v3_660_set_tx_lane_enable,
188 .ctrl_rx_linecfg = ufs_qcom_phy_qmp_v3_660_ctrl_rx_linecfg,
189 .power_control = ufs_qcom_phy_qmp_v3_660_power_control,
190 .dbg_register_dump = ufs_qcom_phy_qmp_v3_660_dbg_register_dump,
191};
192
193static int ufs_qcom_phy_qmp_v3_660_probe(struct platform_device *pdev)
194{
195 struct device *dev = &pdev->dev;
196 struct phy *generic_phy;
197 struct ufs_qcom_phy_qmp_v3_660 *phy;
198 int err = 0;
199
200 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
201 if (!phy) {
202 err = -ENOMEM;
203 goto out;
204 }
205
206 generic_phy = ufs_qcom_phy_generic_probe(pdev, &phy->common_cfg,
207 &ufs_qcom_phy_qmp_v3_660_phy_ops,
208 &phy_v3_660_ops);
209
210 if (!generic_phy) {
211 dev_err(dev, "%s: ufs_qcom_phy_generic_probe() failed\n",
212 __func__);
213 err = -EIO;
214 goto out;
215 }
216
217 phy_set_drvdata(generic_phy, phy);
218
219 strlcpy(phy->common_cfg.name, UFS_PHY_NAME,
220 sizeof(phy->common_cfg.name));
221
222out:
223 return err;
224}
225
226static int ufs_qcom_phy_qmp_v3_660_remove(struct platform_device *pdev)
227{
228 struct device *dev = &pdev->dev;
229 struct phy *generic_phy = to_phy(dev);
230 struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
231 int err = 0;
232
233 err = ufs_qcom_phy_remove(generic_phy, ufs_qcom_phy);
234 if (err)
235 dev_err(dev, "%s: ufs_qcom_phy_remove failed = %d\n",
236 __func__, err);
237
238 return err;
239}
240
241static const struct of_device_id ufs_qcom_phy_qmp_v3_660_of_match[] = {
242 {.compatible = "qcom,ufs-phy-qmp-v3-660"},
243 {},
244};
245MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_v3_660_of_match);
246
247static struct platform_driver ufs_qcom_phy_qmp_v3_660_driver = {
248 .probe = ufs_qcom_phy_qmp_v3_660_probe,
249 .remove = ufs_qcom_phy_qmp_v3_660_remove,
250 .driver = {
251 .of_match_table = ufs_qcom_phy_qmp_v3_660_of_match,
252 .name = "ufs_qcom_phy_qmp_v3_660",
253 .owner = THIS_MODULE,
254 },
255};
256
257module_platform_driver(ufs_qcom_phy_qmp_v3_660_driver);
258
259MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP v3 660");
260MODULE_LICENSE("GPL v2");