This source file includes following definitions.
- ufs_mtk_cfg_unipro_cg
- ufs_mtk_bind_mphy
- ufs_mtk_setup_clocks
- ufs_mtk_init
- ufs_mtk_pre_pwr_change
- ufs_mtk_pwr_change_notify
- ufs_mtk_pre_link
- ufs_mtk_post_link
- ufs_mtk_link_startup_notify
- ufs_mtk_suspend
- ufs_mtk_resume
- ufs_mtk_apply_dev_quirks
- ufs_mtk_probe
- ufs_mtk_remove
1
2
3
4
5
6
7
8
9 #include <linux/of.h>
10 #include <linux/of_address.h>
11 #include <linux/phy/phy.h>
12 #include <linux/platform_device.h>
13
14 #include "ufshcd.h"
15 #include "ufshcd-pltfrm.h"
16 #include "ufs_quirks.h"
17 #include "unipro.h"
18 #include "ufs-mediatek.h"
19
20 static void ufs_mtk_cfg_unipro_cg(struct ufs_hba *hba, bool enable)
21 {
22 u32 tmp;
23
24 if (enable) {
25 ufshcd_dme_get(hba,
26 UIC_ARG_MIB(VS_SAVEPOWERCONTROL), &tmp);
27 tmp = tmp |
28 (1 << RX_SYMBOL_CLK_GATE_EN) |
29 (1 << SYS_CLK_GATE_EN) |
30 (1 << TX_CLK_GATE_EN);
31 ufshcd_dme_set(hba,
32 UIC_ARG_MIB(VS_SAVEPOWERCONTROL), tmp);
33
34 ufshcd_dme_get(hba,
35 UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), &tmp);
36 tmp = tmp & ~(1 << TX_SYMBOL_CLK_REQ_FORCE);
37 ufshcd_dme_set(hba,
38 UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), tmp);
39 } else {
40 ufshcd_dme_get(hba,
41 UIC_ARG_MIB(VS_SAVEPOWERCONTROL), &tmp);
42 tmp = tmp & ~((1 << RX_SYMBOL_CLK_GATE_EN) |
43 (1 << SYS_CLK_GATE_EN) |
44 (1 << TX_CLK_GATE_EN));
45 ufshcd_dme_set(hba,
46 UIC_ARG_MIB(VS_SAVEPOWERCONTROL), tmp);
47
48 ufshcd_dme_get(hba,
49 UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), &tmp);
50 tmp = tmp | (1 << TX_SYMBOL_CLK_REQ_FORCE);
51 ufshcd_dme_set(hba,
52 UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), tmp);
53 }
54 }
55
56 static int ufs_mtk_bind_mphy(struct ufs_hba *hba)
57 {
58 struct ufs_mtk_host *host = ufshcd_get_variant(hba);
59 struct device *dev = hba->dev;
60 struct device_node *np = dev->of_node;
61 int err = 0;
62
63 host->mphy = devm_of_phy_get_by_index(dev, np, 0);
64
65 if (host->mphy == ERR_PTR(-EPROBE_DEFER)) {
66
67
68
69
70 err = -EPROBE_DEFER;
71 dev_info(dev,
72 "%s: required phy hasn't probed yet. err = %d\n",
73 __func__, err);
74 } else if (IS_ERR(host->mphy)) {
75 err = PTR_ERR(host->mphy);
76 dev_info(dev, "%s: PHY get failed %d\n", __func__, err);
77 }
78
79 if (err)
80 host->mphy = NULL;
81
82 return err;
83 }
84
85
86
87
88
89
90
91
92
93 static int ufs_mtk_setup_clocks(struct ufs_hba *hba, bool on,
94 enum ufs_notify_change_status status)
95 {
96 struct ufs_mtk_host *host = ufshcd_get_variant(hba);
97 int ret = -EINVAL;
98
99
100
101
102
103
104 if (!host)
105 return 0;
106
107 switch (status) {
108 case PRE_CHANGE:
109 if (!on)
110 ret = phy_power_off(host->mphy);
111 break;
112 case POST_CHANGE:
113 if (on)
114 ret = phy_power_on(host->mphy);
115 break;
116 }
117
118 return ret;
119 }
120
121
122
123
124
125
126
127
128
129
130
131 static int ufs_mtk_init(struct ufs_hba *hba)
132 {
133 struct ufs_mtk_host *host;
134 struct device *dev = hba->dev;
135 int err = 0;
136
137 host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
138 if (!host) {
139 err = -ENOMEM;
140 dev_info(dev, "%s: no memory for mtk ufs host\n", __func__);
141 goto out;
142 }
143
144 host->hba = hba;
145 ufshcd_set_variant(hba, host);
146
147 err = ufs_mtk_bind_mphy(hba);
148 if (err)
149 goto out_variant_clear;
150
151
152
153
154
155
156
157
158 ufs_mtk_setup_clocks(hba, true, POST_CHANGE);
159
160 goto out;
161
162 out_variant_clear:
163 ufshcd_set_variant(hba, NULL);
164 out:
165 return err;
166 }
167
168 static int ufs_mtk_pre_pwr_change(struct ufs_hba *hba,
169 struct ufs_pa_layer_attr *dev_max_params,
170 struct ufs_pa_layer_attr *dev_req_params)
171 {
172 struct ufs_dev_params host_cap;
173 int ret;
174
175 host_cap.tx_lanes = UFS_MTK_LIMIT_NUM_LANES_TX;
176 host_cap.rx_lanes = UFS_MTK_LIMIT_NUM_LANES_RX;
177 host_cap.hs_rx_gear = UFS_MTK_LIMIT_HSGEAR_RX;
178 host_cap.hs_tx_gear = UFS_MTK_LIMIT_HSGEAR_TX;
179 host_cap.pwm_rx_gear = UFS_MTK_LIMIT_PWMGEAR_RX;
180 host_cap.pwm_tx_gear = UFS_MTK_LIMIT_PWMGEAR_TX;
181 host_cap.rx_pwr_pwm = UFS_MTK_LIMIT_RX_PWR_PWM;
182 host_cap.tx_pwr_pwm = UFS_MTK_LIMIT_TX_PWR_PWM;
183 host_cap.rx_pwr_hs = UFS_MTK_LIMIT_RX_PWR_HS;
184 host_cap.tx_pwr_hs = UFS_MTK_LIMIT_TX_PWR_HS;
185 host_cap.hs_rate = UFS_MTK_LIMIT_HS_RATE;
186 host_cap.desired_working_mode =
187 UFS_MTK_LIMIT_DESIRED_MODE;
188
189 ret = ufshcd_get_pwr_dev_param(&host_cap,
190 dev_max_params,
191 dev_req_params);
192 if (ret) {
193 pr_info("%s: failed to determine capabilities\n",
194 __func__);
195 }
196
197 return ret;
198 }
199
200 static int ufs_mtk_pwr_change_notify(struct ufs_hba *hba,
201 enum ufs_notify_change_status stage,
202 struct ufs_pa_layer_attr *dev_max_params,
203 struct ufs_pa_layer_attr *dev_req_params)
204 {
205 int ret = 0;
206
207 switch (stage) {
208 case PRE_CHANGE:
209 ret = ufs_mtk_pre_pwr_change(hba, dev_max_params,
210 dev_req_params);
211 break;
212 case POST_CHANGE:
213 break;
214 default:
215 ret = -EINVAL;
216 break;
217 }
218
219 return ret;
220 }
221
222 static int ufs_mtk_pre_link(struct ufs_hba *hba)
223 {
224 int ret;
225 u32 tmp;
226
227
228 ret = ufshcd_dme_get(hba, UIC_ARG_MIB(VS_SAVEPOWERCONTROL), &tmp);
229 if (ret)
230 return ret;
231
232 tmp &= ~(1 << 6);
233
234 ret = ufshcd_dme_set(hba, UIC_ARG_MIB(VS_SAVEPOWERCONTROL), tmp);
235
236 return ret;
237 }
238
239 static int ufs_mtk_post_link(struct ufs_hba *hba)
240 {
241
242 ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0);
243
244
245 ufs_mtk_cfg_unipro_cg(hba, true);
246
247 return 0;
248 }
249
250 static int ufs_mtk_link_startup_notify(struct ufs_hba *hba,
251 enum ufs_notify_change_status stage)
252 {
253 int ret = 0;
254
255 switch (stage) {
256 case PRE_CHANGE:
257 ret = ufs_mtk_pre_link(hba);
258 break;
259 case POST_CHANGE:
260 ret = ufs_mtk_post_link(hba);
261 break;
262 default:
263 ret = -EINVAL;
264 break;
265 }
266
267 return ret;
268 }
269
270 static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
271 {
272 struct ufs_mtk_host *host = ufshcd_get_variant(hba);
273
274 if (ufshcd_is_link_hibern8(hba))
275 phy_power_off(host->mphy);
276
277 return 0;
278 }
279
280 static int ufs_mtk_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
281 {
282 struct ufs_mtk_host *host = ufshcd_get_variant(hba);
283
284 if (ufshcd_is_link_hibern8(hba))
285 phy_power_on(host->mphy);
286
287 return 0;
288 }
289
290 static int ufs_mtk_apply_dev_quirks(struct ufs_hba *hba,
291 struct ufs_dev_desc *card)
292 {
293 if (card->wmanufacturerid == UFS_VENDOR_SAMSUNG)
294 ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TACTIVATE), 6);
295
296 return 0;
297 }
298
299
300
301
302
303
304
305 static struct ufs_hba_variant_ops ufs_hba_mtk_vops = {
306 .name = "mediatek.ufshci",
307 .init = ufs_mtk_init,
308 .setup_clocks = ufs_mtk_setup_clocks,
309 .link_startup_notify = ufs_mtk_link_startup_notify,
310 .pwr_change_notify = ufs_mtk_pwr_change_notify,
311 .apply_dev_quirks = ufs_mtk_apply_dev_quirks,
312 .suspend = ufs_mtk_suspend,
313 .resume = ufs_mtk_resume,
314 };
315
316
317
318
319
320
321
322 static int ufs_mtk_probe(struct platform_device *pdev)
323 {
324 int err;
325 struct device *dev = &pdev->dev;
326
327
328 err = ufshcd_pltfrm_init(pdev, &ufs_hba_mtk_vops);
329 if (err)
330 dev_info(dev, "probe failed %d\n", err);
331
332 return err;
333 }
334
335
336
337
338
339
340
341 static int ufs_mtk_remove(struct platform_device *pdev)
342 {
343 struct ufs_hba *hba = platform_get_drvdata(pdev);
344
345 pm_runtime_get_sync(&(pdev)->dev);
346 ufshcd_remove(hba);
347 return 0;
348 }
349
350 static const struct of_device_id ufs_mtk_of_match[] = {
351 { .compatible = "mediatek,mt8183-ufshci"},
352 {},
353 };
354
355 static const struct dev_pm_ops ufs_mtk_pm_ops = {
356 .suspend = ufshcd_pltfrm_suspend,
357 .resume = ufshcd_pltfrm_resume,
358 .runtime_suspend = ufshcd_pltfrm_runtime_suspend,
359 .runtime_resume = ufshcd_pltfrm_runtime_resume,
360 .runtime_idle = ufshcd_pltfrm_runtime_idle,
361 };
362
363 static struct platform_driver ufs_mtk_pltform = {
364 .probe = ufs_mtk_probe,
365 .remove = ufs_mtk_remove,
366 .shutdown = ufshcd_pltfrm_shutdown,
367 .driver = {
368 .name = "ufshcd-mtk",
369 .pm = &ufs_mtk_pm_ops,
370 .of_match_table = ufs_mtk_of_match,
371 },
372 };
373
374 MODULE_AUTHOR("Stanley Chu <stanley.chu@mediatek.com>");
375 MODULE_AUTHOR("Peter Wang <peter.wang@mediatek.com>");
376 MODULE_DESCRIPTION("MediaTek UFS Host Driver");
377 MODULE_LICENSE("GPL v2");
378
379 module_platform_driver(ufs_mtk_pltform);