blob: 6b8dbc29f6e86d9bede4224b0a2bad6c0ce52ba3 [file] [log] [blame]
Subhash Jadavanicce6fbc2016-08-11 11:35:26 -07001/*
2 * Copyright (c) 2013-2016, 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.h"
16
17#define UFS_PHY_NAME "ufs_phy_qmp_v3"
18
19static
20int ufs_qcom_phy_qmp_v3_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
27 tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
28 tbl_B = phy_cal_table_rate_B;
29
30 tbl_A = phy_cal_table_rate_A;
31 tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A);
32
33 err = ufs_qcom_phy_calibrate(ufs_qcom_phy,
34 tbl_A, tbl_size_A,
35 tbl_B, tbl_size_B,
36 is_rate_B);
37
38 if (err)
39 dev_err(ufs_qcom_phy->dev,
40 "%s: ufs_qcom_phy_calibrate() failed %d\n",
41 __func__, err);
42 return err;
43}
44
45static int ufs_qcom_phy_qmp_v3_init(struct phy *generic_phy)
46{
47 struct ufs_qcom_phy_qmp_v3 *phy = phy_get_drvdata(generic_phy);
48 struct ufs_qcom_phy *phy_common = &phy->common_cfg;
49 int err;
50
51 err = ufs_qcom_phy_init_clks(generic_phy, phy_common);
52 if (err) {
53 dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_clks() failed %d\n",
54 __func__, err);
55 goto out;
56 }
57
58 err = ufs_qcom_phy_init_vregulators(generic_phy, phy_common);
59 if (err) {
60 dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_vregulators() failed %d\n",
61 __func__, err);
62 goto out;
63 }
64
65out:
66 return err;
67}
68
69static
70void ufs_qcom_phy_qmp_v3_power_control(struct ufs_qcom_phy *phy,
71 bool power_ctrl)
72{
73 if (!power_ctrl) {
74 /* apply analog power collapse */
75 writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
76 /*
77 * Make sure that PHY knows its analog rail is going to be
78 * powered OFF.
79 */
80 mb();
81 } else {
82 /* bring PHY out of analog power collapse */
83 writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
84
85 /*
86 * Before any transactions involving PHY, ensure PHY knows
87 * that it's analog rail is powered ON.
88 */
89 mb();
90 }
91}
92
93static inline
94void ufs_qcom_phy_qmp_v3_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val)
95{
96 /*
97 * v3 PHY does not have TX_LANE_ENABLE register.
98 * Implement this function so as not to propagate error to caller.
99 */
100}
101
102static
103void ufs_qcom_phy_qmp_v3_ctrl_rx_linecfg(struct ufs_qcom_phy *phy, bool ctrl)
104{
105 u32 temp;
106
107 temp = readl_relaxed(phy->mmio + UFS_PHY_LINECFG_DISABLE);
108
109 if (ctrl) /* enable RX LineCfg */
110 temp &= ~UFS_PHY_RX_LINECFG_DISABLE_BIT;
111 else /* disable RX LineCfg */
112 temp |= UFS_PHY_RX_LINECFG_DISABLE_BIT;
113
114 writel_relaxed(temp, phy->mmio + UFS_PHY_LINECFG_DISABLE);
115 /* make sure that RX LineCfg config applied before we return */
116 mb();
117}
118
119static inline void ufs_qcom_phy_qmp_v3_start_serdes(struct ufs_qcom_phy *phy)
120{
121 u32 tmp;
122
123 tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START);
124 tmp &= ~MASK_SERDES_START;
125 tmp |= (1 << OFFSET_SERDES_START);
126 writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START);
127 /* Ensure register value is committed */
128 mb();
129}
130
131static int ufs_qcom_phy_qmp_v3_is_pcs_ready(struct ufs_qcom_phy *phy_common)
132{
133 int err = 0;
134 u32 val;
135
136 err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS,
137 val, (val & MASK_PCS_READY), 10, 1000000);
138 if (err) {
139 dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n",
140 __func__, err);
141 goto out;
142 }
143
144out:
145 return err;
146}
147
148static
149int ufs_qcom_phy_qmp_v3_configure_lpm(struct ufs_qcom_phy *ufs_qcom_phy,
150 bool enable)
151{
152 int err = 0;
153 int tbl_size;
154 struct ufs_qcom_phy_calibration *tbl = NULL;
155
156 /* The default low power mode configuration is SVS2 */
157 if (enable) {
158 tbl_size = ARRAY_SIZE(phy_cal_table_svs2_enable);
159 tbl = phy_cal_table_svs2_enable;
160 } else {
161 tbl_size = ARRAY_SIZE(phy_cal_table_svs2_disable);
162 tbl = phy_cal_table_svs2_disable;
163 }
164
165 if (!tbl) {
166 dev_err(ufs_qcom_phy->dev, "%s: tbl for SVS2 %s is NULL",
167 __func__, enable ? "enable" : "disable");
168 err = -EINVAL;
169 goto out;
170 }
171
172 ufs_qcom_phy_write_tbl(ufs_qcom_phy, tbl, tbl_size);
173
174 /* flush buffered writes */
175 mb();
176
177out:
178 return err;
179}
180
181struct phy_ops ufs_qcom_phy_qmp_v3_phy_ops = {
182 .init = ufs_qcom_phy_qmp_v3_init,
183 .exit = ufs_qcom_phy_exit,
184 .power_on = ufs_qcom_phy_power_on,
185 .power_off = ufs_qcom_phy_power_off,
186 .owner = THIS_MODULE,
187};
188
189struct ufs_qcom_phy_specific_ops phy_v3_ops = {
190 .calibrate_phy = ufs_qcom_phy_qmp_v3_phy_calibrate,
191 .start_serdes = ufs_qcom_phy_qmp_v3_start_serdes,
192 .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_v3_is_pcs_ready,
193 .set_tx_lane_enable = ufs_qcom_phy_qmp_v3_set_tx_lane_enable,
194 .ctrl_rx_linecfg = ufs_qcom_phy_qmp_v3_ctrl_rx_linecfg,
195 .power_control = ufs_qcom_phy_qmp_v3_power_control,
196 .configure_lpm = ufs_qcom_phy_qmp_v3_configure_lpm,
197};
198
199static int ufs_qcom_phy_qmp_v3_probe(struct platform_device *pdev)
200{
201 struct device *dev = &pdev->dev;
202 struct phy *generic_phy;
203 struct ufs_qcom_phy_qmp_v3 *phy;
204 int err = 0;
205
206 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
207 if (!phy) {
208 err = -ENOMEM;
209 goto out;
210 }
211
212 generic_phy = ufs_qcom_phy_generic_probe(pdev, &phy->common_cfg,
213 &ufs_qcom_phy_qmp_v3_phy_ops, &phy_v3_ops);
214
215 if (!generic_phy) {
216 dev_err(dev, "%s: ufs_qcom_phy_generic_probe() failed\n",
217 __func__);
218 err = -EIO;
219 goto out;
220 }
221
222 phy_set_drvdata(generic_phy, phy);
223
224 strlcpy(phy->common_cfg.name, UFS_PHY_NAME,
225 sizeof(phy->common_cfg.name));
226
227out:
228 return err;
229}
230
231static int ufs_qcom_phy_qmp_v3_remove(struct platform_device *pdev)
232{
233 struct device *dev = &pdev->dev;
234 struct phy *generic_phy = to_phy(dev);
235 struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
236 int err = 0;
237
238 err = ufs_qcom_phy_remove(generic_phy, ufs_qcom_phy);
239 if (err)
240 dev_err(dev, "%s: ufs_qcom_phy_remove failed = %d\n",
241 __func__, err);
242
243 return err;
244}
245
246static const struct of_device_id ufs_qcom_phy_qmp_v3_of_match[] = {
247 {.compatible = "qcom,ufs-phy-qmp-v3"},
248 {},
249};
250MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_v3_of_match);
251
252static struct platform_driver ufs_qcom_phy_qmp_v3_driver = {
253 .probe = ufs_qcom_phy_qmp_v3_probe,
254 .remove = ufs_qcom_phy_qmp_v3_remove,
255 .driver = {
256 .of_match_table = ufs_qcom_phy_qmp_v3_of_match,
257 .name = "ufs_qcom_phy_qmp_v3",
258 .owner = THIS_MODULE,
259 },
260};
261
262module_platform_driver(ufs_qcom_phy_qmp_v3_driver);
263
264MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP v3");
265MODULE_LICENSE("GPL v2");