1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * HiSilicon Hixxxx UFS Driver |
4 | * |
5 | * Copyright (c) 2016-2017 Linaro Ltd. |
6 | * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. |
7 | */ |
8 | |
9 | #include <linux/time.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/module.h> |
12 | #include <linux/of.h> |
13 | #include <linux/of_address.h> |
14 | #include <linux/dma-mapping.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/reset.h> |
17 | |
18 | #include <ufs/ufshcd.h> |
19 | #include "ufshcd-pltfrm.h" |
20 | #include <ufs/unipro.h> |
21 | #include "ufs-hisi.h" |
22 | #include <ufs/ufshci.h> |
23 | #include <ufs/ufs_quirks.h> |
24 | |
25 | static int ufs_hisi_check_hibern8(struct ufs_hba *hba) |
26 | { |
27 | int err = 0; |
28 | u32 tx_fsm_val_0 = 0; |
29 | u32 tx_fsm_val_1 = 0; |
30 | unsigned long timeout = jiffies + msecs_to_jiffies(HBRN8_POLL_TOUT_MS); |
31 | |
32 | do { |
33 | err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 0), |
34 | mib_val: &tx_fsm_val_0); |
35 | err |= ufshcd_dme_get(hba, |
36 | UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 1), mib_val: &tx_fsm_val_1); |
37 | if (err || (tx_fsm_val_0 == TX_FSM_HIBERN8 && |
38 | tx_fsm_val_1 == TX_FSM_HIBERN8)) |
39 | break; |
40 | |
41 | /* sleep for max. 200us */ |
42 | usleep_range(min: 100, max: 200); |
43 | } while (time_before(jiffies, timeout)); |
44 | |
45 | /* |
46 | * we might have scheduled out for long during polling so |
47 | * check the state again. |
48 | */ |
49 | if (time_after(jiffies, timeout)) { |
50 | err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 0), |
51 | mib_val: &tx_fsm_val_0); |
52 | err |= ufshcd_dme_get(hba, |
53 | UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 1), mib_val: &tx_fsm_val_1); |
54 | } |
55 | |
56 | if (err) { |
57 | dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n" , |
58 | __func__, err); |
59 | } else if (tx_fsm_val_0 != TX_FSM_HIBERN8 || |
60 | tx_fsm_val_1 != TX_FSM_HIBERN8) { |
61 | err = -1; |
62 | dev_err(hba->dev, "%s: invalid TX_FSM_STATE, lane0 = %d, lane1 = %d\n" , |
63 | __func__, tx_fsm_val_0, tx_fsm_val_1); |
64 | } |
65 | |
66 | return err; |
67 | } |
68 | |
69 | static void ufs_hisi_clk_init(struct ufs_hba *hba) |
70 | { |
71 | struct ufs_hisi_host *host = ufshcd_get_variant(hba); |
72 | |
73 | ufs_sys_ctrl_clr_bits(host, BIT_SYSCTRL_REF_CLOCK_EN, PHY_CLK_CTRL); |
74 | if (ufs_sys_ctrl_readl(host, PHY_CLK_CTRL) & BIT_SYSCTRL_REF_CLOCK_EN) |
75 | mdelay(1); |
76 | /* use abb clk */ |
77 | ufs_sys_ctrl_clr_bits(host, BIT_UFS_REFCLK_SRC_SEl, UFS_SYSCTRL); |
78 | ufs_sys_ctrl_clr_bits(host, BIT_UFS_REFCLK_ISO_EN, PHY_ISO_EN); |
79 | /* open mphy ref clk */ |
80 | ufs_sys_ctrl_set_bits(host, BIT_SYSCTRL_REF_CLOCK_EN, PHY_CLK_CTRL); |
81 | } |
82 | |
83 | static void ufs_hisi_soc_init(struct ufs_hba *hba) |
84 | { |
85 | struct ufs_hisi_host *host = ufshcd_get_variant(hba); |
86 | u32 reg; |
87 | |
88 | if (!IS_ERR(ptr: host->rst)) |
89 | reset_control_assert(rstc: host->rst); |
90 | |
91 | /* HC_PSW powerup */ |
92 | ufs_sys_ctrl_set_bits(host, BIT_UFS_PSW_MTCMOS_EN, PSW_POWER_CTRL); |
93 | udelay(10); |
94 | /* notify PWR ready */ |
95 | ufs_sys_ctrl_set_bits(host, BIT_SYSCTRL_PWR_READY, HC_LP_CTRL); |
96 | ufs_sys_ctrl_writel(host, MASK_UFS_DEVICE_RESET | 0, |
97 | UFS_DEVICE_RESET_CTRL); |
98 | |
99 | reg = ufs_sys_ctrl_readl(host, PHY_CLK_CTRL); |
100 | reg = (reg & ~MASK_SYSCTRL_CFG_CLOCK_FREQ) | UFS_FREQ_CFG_CLK; |
101 | /* set cfg clk freq */ |
102 | ufs_sys_ctrl_writel(host, reg, PHY_CLK_CTRL); |
103 | /* set ref clk freq */ |
104 | ufs_sys_ctrl_clr_bits(host, MASK_SYSCTRL_REF_CLOCK_SEL, PHY_CLK_CTRL); |
105 | /* bypass ufs clk gate */ |
106 | ufs_sys_ctrl_set_bits(host, MASK_UFS_CLK_GATE_BYPASS, |
107 | CLOCK_GATE_BYPASS); |
108 | ufs_sys_ctrl_set_bits(host, MASK_UFS_SYSCRTL_BYPASS, UFS_SYSCTRL); |
109 | |
110 | /* open psw clk */ |
111 | ufs_sys_ctrl_set_bits(host, BIT_SYSCTRL_PSW_CLK_EN, PSW_CLK_CTRL); |
112 | /* disable ufshc iso */ |
113 | ufs_sys_ctrl_clr_bits(host, BIT_UFS_PSW_ISO_CTRL, PSW_POWER_CTRL); |
114 | /* disable phy iso */ |
115 | ufs_sys_ctrl_clr_bits(host, BIT_UFS_PHY_ISO_CTRL, PHY_ISO_EN); |
116 | /* notice iso disable */ |
117 | ufs_sys_ctrl_clr_bits(host, BIT_SYSCTRL_LP_ISOL_EN, HC_LP_CTRL); |
118 | |
119 | /* disable lp_reset_n */ |
120 | ufs_sys_ctrl_set_bits(host, BIT_SYSCTRL_LP_RESET_N, RESET_CTRL_EN); |
121 | mdelay(1); |
122 | |
123 | ufs_sys_ctrl_writel(host, MASK_UFS_DEVICE_RESET | BIT_UFS_DEVICE_RESET, |
124 | UFS_DEVICE_RESET_CTRL); |
125 | |
126 | msleep(msecs: 20); |
127 | |
128 | /* |
129 | * enable the fix of linereset recovery, |
130 | * and enable rx_reset/tx_rest beat |
131 | * enable ref_clk_en override(bit5) & |
132 | * override value = 1(bit4), with mask |
133 | */ |
134 | ufs_sys_ctrl_writel(host, 0x03300330, UFS_DEVICE_RESET_CTRL); |
135 | |
136 | if (!IS_ERR(ptr: host->rst)) |
137 | reset_control_deassert(rstc: host->rst); |
138 | } |
139 | |
140 | static int ufs_hisi_link_startup_pre_change(struct ufs_hba *hba) |
141 | { |
142 | struct ufs_hisi_host *host = ufshcd_get_variant(hba); |
143 | int err; |
144 | uint32_t value; |
145 | uint32_t reg; |
146 | |
147 | /* Unipro VS_mphy_disable */ |
148 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD0C1, 0x0), mib_val: 0x1); |
149 | /* PA_HSSeries */ |
150 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x156A, 0x0), mib_val: 0x2); |
151 | /* MPHY CBRATESEL */ |
152 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8114, 0x0), mib_val: 0x1); |
153 | /* MPHY CBOVRCTRL2 */ |
154 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8121, 0x0), mib_val: 0x2D); |
155 | /* MPHY CBOVRCTRL3 */ |
156 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8122, 0x0), mib_val: 0x1); |
157 | |
158 | if (host->caps & UFS_HISI_CAP_PHY10nm) { |
159 | /* MPHY CBOVRCTRL4 */ |
160 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8127, 0x0), mib_val: 0x98); |
161 | /* MPHY CBOVRCTRL5 */ |
162 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8128, 0x0), mib_val: 0x1); |
163 | } |
164 | |
165 | /* Unipro VS_MphyCfgUpdt */ |
166 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD085, 0x0), mib_val: 0x1); |
167 | /* MPHY RXOVRCTRL4 rx0 */ |
168 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x800D, 0x4), mib_val: 0x58); |
169 | /* MPHY RXOVRCTRL4 rx1 */ |
170 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x800D, 0x5), mib_val: 0x58); |
171 | /* MPHY RXOVRCTRL5 rx0 */ |
172 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x800E, 0x4), mib_val: 0xB); |
173 | /* MPHY RXOVRCTRL5 rx1 */ |
174 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x800E, 0x5), mib_val: 0xB); |
175 | /* MPHY RXSQCONTROL rx0 */ |
176 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8009, 0x4), mib_val: 0x1); |
177 | /* MPHY RXSQCONTROL rx1 */ |
178 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8009, 0x5), mib_val: 0x1); |
179 | /* Unipro VS_MphyCfgUpdt */ |
180 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD085, 0x0), mib_val: 0x1); |
181 | |
182 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8113, 0x0), mib_val: 0x1); |
183 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD085, 0x0), mib_val: 0x1); |
184 | |
185 | if (host->caps & UFS_HISI_CAP_PHY10nm) { |
186 | /* RX_Hibern8Time_Capability*/ |
187 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0092, 0x4), mib_val: 0xA); |
188 | /* RX_Hibern8Time_Capability*/ |
189 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0092, 0x5), mib_val: 0xA); |
190 | /* RX_Min_ActivateTime */ |
191 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008f, 0x4), mib_val: 0xA); |
192 | /* RX_Min_ActivateTime*/ |
193 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008f, 0x5), mib_val: 0xA); |
194 | } else { |
195 | /* Tactive RX */ |
196 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008F, 0x4), mib_val: 0x7); |
197 | /* Tactive RX */ |
198 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008F, 0x5), mib_val: 0x7); |
199 | } |
200 | |
201 | /* Gear3 Synclength */ |
202 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0095, 0x4), mib_val: 0x4F); |
203 | /* Gear3 Synclength */ |
204 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0095, 0x5), mib_val: 0x4F); |
205 | /* Gear2 Synclength */ |
206 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0094, 0x4), mib_val: 0x4F); |
207 | /* Gear2 Synclength */ |
208 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0094, 0x5), mib_val: 0x4F); |
209 | /* Gear1 Synclength */ |
210 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008B, 0x4), mib_val: 0x4F); |
211 | /* Gear1 Synclength */ |
212 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008B, 0x5), mib_val: 0x4F); |
213 | /* Thibernate Tx */ |
214 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x000F, 0x0), mib_val: 0x5); |
215 | /* Thibernate Tx */ |
216 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x000F, 0x1), mib_val: 0x5); |
217 | |
218 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD085, 0x0), mib_val: 0x1); |
219 | /* Unipro VS_mphy_disable */ |
220 | ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(0xD0C1, 0x0), mib_val: &value); |
221 | if (value != 0x1) |
222 | dev_info(hba->dev, |
223 | "Warring!!! Unipro VS_mphy_disable is 0x%x\n" , value); |
224 | |
225 | /* Unipro VS_mphy_disable */ |
226 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD0C1, 0x0), mib_val: 0x0); |
227 | err = ufs_hisi_check_hibern8(hba); |
228 | if (err) |
229 | dev_err(hba->dev, "ufs_hisi_check_hibern8 error\n" ); |
230 | |
231 | if (!(host->caps & UFS_HISI_CAP_PHY10nm)) |
232 | ufshcd_writel(hba, UFS_HCLKDIV_NORMAL_VALUE, UFS_REG_HCLKDIV); |
233 | |
234 | /* disable auto H8 */ |
235 | reg = ufshcd_readl(hba, REG_AUTO_HIBERNATE_IDLE_TIMER); |
236 | reg = reg & (~UFS_AHIT_AH8ITV_MASK); |
237 | ufshcd_writel(hba, reg, REG_AUTO_HIBERNATE_IDLE_TIMER); |
238 | |
239 | /* Unipro PA_Local_TX_LCC_Enable */ |
240 | ufshcd_disable_host_tx_lcc(hba); |
241 | /* close Unipro VS_Mk2ExtnSupport */ |
242 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD0AB, 0x0), mib_val: 0x0); |
243 | ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(0xD0AB, 0x0), mib_val: &value); |
244 | if (value != 0) { |
245 | /* Ensure close success */ |
246 | dev_info(hba->dev, "WARN: close VS_Mk2ExtnSupport failed\n" ); |
247 | } |
248 | |
249 | return err; |
250 | } |
251 | |
252 | static int ufs_hisi_link_startup_post_change(struct ufs_hba *hba) |
253 | { |
254 | struct ufs_hisi_host *host = ufshcd_get_variant(hba); |
255 | |
256 | /* Unipro DL_AFC0CreditThreshold */ |
257 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x2044), mib_val: 0x0); |
258 | /* Unipro DL_TC0OutAckThreshold */ |
259 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x2045), mib_val: 0x0); |
260 | /* Unipro DL_TC0TXFCThreshold */ |
261 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x2040), mib_val: 0x9); |
262 | |
263 | /* not bypass ufs clk gate */ |
264 | ufs_sys_ctrl_clr_bits(host, MASK_UFS_CLK_GATE_BYPASS, |
265 | CLOCK_GATE_BYPASS); |
266 | ufs_sys_ctrl_clr_bits(host, MASK_UFS_SYSCRTL_BYPASS, |
267 | UFS_SYSCTRL); |
268 | |
269 | /* select received symbol cnt */ |
270 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xd09a), mib_val: 0x80000000); |
271 | /* reset counter0 and enable */ |
272 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xd09c), mib_val: 0x00000005); |
273 | |
274 | return 0; |
275 | } |
276 | |
277 | static int ufs_hisi_link_startup_notify(struct ufs_hba *hba, |
278 | enum ufs_notify_change_status status) |
279 | { |
280 | int err = 0; |
281 | |
282 | switch (status) { |
283 | case PRE_CHANGE: |
284 | err = ufs_hisi_link_startup_pre_change(hba); |
285 | break; |
286 | case POST_CHANGE: |
287 | err = ufs_hisi_link_startup_post_change(hba); |
288 | break; |
289 | default: |
290 | break; |
291 | } |
292 | |
293 | return err; |
294 | } |
295 | |
296 | static void ufs_hisi_set_dev_cap(struct ufs_host_params *host_params) |
297 | { |
298 | ufshcd_init_host_params(host_params); |
299 | } |
300 | |
301 | static void ufs_hisi_pwr_change_pre_change(struct ufs_hba *hba) |
302 | { |
303 | struct ufs_hisi_host *host = ufshcd_get_variant(hba); |
304 | |
305 | if (host->caps & UFS_HISI_CAP_PHY10nm) { |
306 | /* |
307 | * Boston platform need to set SaveConfigTime to 0x13, |
308 | * and change sync length to maximum value |
309 | */ |
310 | /* VS_DebugSaveConfigTime */ |
311 | ufshcd_dme_set(hba, UIC_ARG_MIB((u32)0xD0A0), mib_val: 0x13); |
312 | /* g1 sync length */ |
313 | ufshcd_dme_set(hba, UIC_ARG_MIB((u32)0x1552), mib_val: 0x4f); |
314 | /* g2 sync length */ |
315 | ufshcd_dme_set(hba, UIC_ARG_MIB((u32)0x1554), mib_val: 0x4f); |
316 | /* g3 sync length */ |
317 | ufshcd_dme_set(hba, UIC_ARG_MIB((u32)0x1556), mib_val: 0x4f); |
318 | /* PA_Hibern8Time */ |
319 | ufshcd_dme_set(hba, UIC_ARG_MIB((u32)0x15a7), mib_val: 0xA); |
320 | /* PA_Tactivate */ |
321 | ufshcd_dme_set(hba, UIC_ARG_MIB((u32)0x15a8), mib_val: 0xA); |
322 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xd085, 0x0), mib_val: 0x01); |
323 | } |
324 | |
325 | if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_VS_DEBUGSAVECONFIGTIME) { |
326 | pr_info("ufs flash device must set VS_DebugSaveConfigTime 0x10\n" ); |
327 | /* VS_DebugSaveConfigTime */ |
328 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xD0A0), mib_val: 0x10); |
329 | /* sync length */ |
330 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x1556), mib_val: 0x48); |
331 | } |
332 | |
333 | /* update */ |
334 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x15A8), mib_val: 0x1); |
335 | /* PA_TxSkip */ |
336 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x155c), mib_val: 0x0); |
337 | /*PA_PWRModeUserData0 = 8191, default is 0*/ |
338 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x15b0), SZ_8K - 1); |
339 | /*PA_PWRModeUserData1 = 65535, default is 0*/ |
340 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x15b1), SZ_64K - 1); |
341 | /*PA_PWRModeUserData2 = 32767, default is 0*/ |
342 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x15b2), SZ_32K - 1); |
343 | /*DME_FC0ProtectionTimeOutVal = 8191, default is 0*/ |
344 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xd041), SZ_8K - 1); |
345 | /*DME_TC0ReplayTimeOutVal = 65535, default is 0*/ |
346 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xd042), SZ_64K - 1); |
347 | /*DME_AFC0ReqTimeOutVal = 32767, default is 0*/ |
348 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xd043), SZ_32K - 1); |
349 | /*PA_PWRModeUserData3 = 8191, default is 0*/ |
350 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x15b3), SZ_8K - 1); |
351 | /*PA_PWRModeUserData4 = 65535, default is 0*/ |
352 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x15b4), SZ_64K - 1); |
353 | /*PA_PWRModeUserData5 = 32767, default is 0*/ |
354 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x15b5), SZ_32K - 1); |
355 | /*DME_FC1ProtectionTimeOutVal = 8191, default is 0*/ |
356 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xd044), SZ_8K - 1); |
357 | /*DME_TC1ReplayTimeOutVal = 65535, default is 0*/ |
358 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xd045), SZ_64K - 1); |
359 | /*DME_AFC1ReqTimeOutVal = 32767, default is 0*/ |
360 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xd046), SZ_32K - 1); |
361 | } |
362 | |
363 | static int ufs_hisi_pwr_change_notify(struct ufs_hba *hba, |
364 | enum ufs_notify_change_status status, |
365 | struct ufs_pa_layer_attr *dev_max_params, |
366 | struct ufs_pa_layer_attr *dev_req_params) |
367 | { |
368 | struct ufs_host_params host_params; |
369 | int ret = 0; |
370 | |
371 | if (!dev_req_params) { |
372 | dev_err(hba->dev, |
373 | "%s: incoming dev_req_params is NULL\n" , __func__); |
374 | ret = -EINVAL; |
375 | goto out; |
376 | } |
377 | |
378 | switch (status) { |
379 | case PRE_CHANGE: |
380 | ufs_hisi_set_dev_cap(host_params: &host_params); |
381 | ret = ufshcd_negotiate_pwr_params(host_params: &host_params, dev_max: dev_max_params, agreed_pwr: dev_req_params); |
382 | if (ret) { |
383 | dev_err(hba->dev, |
384 | "%s: failed to determine capabilities\n" , __func__); |
385 | goto out; |
386 | } |
387 | |
388 | ufs_hisi_pwr_change_pre_change(hba); |
389 | break; |
390 | case POST_CHANGE: |
391 | break; |
392 | default: |
393 | ret = -EINVAL; |
394 | break; |
395 | } |
396 | out: |
397 | return ret; |
398 | } |
399 | |
400 | static int ufs_hisi_suspend_prepare(struct device *dev) |
401 | { |
402 | /* RPM and SPM are different. Refer ufs_hisi_suspend() */ |
403 | return __ufshcd_suspend_prepare(dev, rpm_ok_for_spm: false); |
404 | } |
405 | |
406 | static int ufs_hisi_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op, |
407 | enum ufs_notify_change_status status) |
408 | { |
409 | struct ufs_hisi_host *host = ufshcd_get_variant(hba); |
410 | |
411 | if (status == PRE_CHANGE) |
412 | return 0; |
413 | |
414 | if (pm_op == UFS_RUNTIME_PM) |
415 | return 0; |
416 | |
417 | if (host->in_suspend) { |
418 | WARN_ON(1); |
419 | return 0; |
420 | } |
421 | |
422 | ufs_sys_ctrl_clr_bits(host, BIT_SYSCTRL_REF_CLOCK_EN, PHY_CLK_CTRL); |
423 | udelay(10); |
424 | /* set ref_dig_clk override of PHY PCS to 0 */ |
425 | ufs_sys_ctrl_writel(host, 0x00100000, UFS_DEVICE_RESET_CTRL); |
426 | |
427 | host->in_suspend = true; |
428 | |
429 | return 0; |
430 | } |
431 | |
432 | static int ufs_hisi_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) |
433 | { |
434 | struct ufs_hisi_host *host = ufshcd_get_variant(hba); |
435 | |
436 | if (!host->in_suspend) |
437 | return 0; |
438 | |
439 | /* set ref_dig_clk override of PHY PCS to 1 */ |
440 | ufs_sys_ctrl_writel(host, 0x00100010, UFS_DEVICE_RESET_CTRL); |
441 | udelay(10); |
442 | ufs_sys_ctrl_set_bits(host, BIT_SYSCTRL_REF_CLOCK_EN, PHY_CLK_CTRL); |
443 | |
444 | host->in_suspend = false; |
445 | return 0; |
446 | } |
447 | |
448 | static int ufs_hisi_get_resource(struct ufs_hisi_host *host) |
449 | { |
450 | struct device *dev = host->hba->dev; |
451 | struct platform_device *pdev = to_platform_device(dev); |
452 | |
453 | /* get resource of ufs sys ctrl */ |
454 | host->ufs_sys_ctrl = devm_platform_ioremap_resource(pdev, index: 1); |
455 | return PTR_ERR_OR_ZERO(ptr: host->ufs_sys_ctrl); |
456 | } |
457 | |
458 | static void ufs_hisi_set_pm_lvl(struct ufs_hba *hba) |
459 | { |
460 | hba->rpm_lvl = UFS_PM_LVL_1; |
461 | hba->spm_lvl = UFS_PM_LVL_3; |
462 | } |
463 | |
464 | /** |
465 | * ufs_hisi_init_common |
466 | * @hba: host controller instance |
467 | */ |
468 | static int ufs_hisi_init_common(struct ufs_hba *hba) |
469 | { |
470 | int err = 0; |
471 | struct device *dev = hba->dev; |
472 | struct ufs_hisi_host *host; |
473 | |
474 | host = devm_kzalloc(dev, size: sizeof(*host), GFP_KERNEL); |
475 | if (!host) |
476 | return -ENOMEM; |
477 | |
478 | host->hba = hba; |
479 | ufshcd_set_variant(hba, variant: host); |
480 | |
481 | host->rst = devm_reset_control_get(dev, id: "rst" ); |
482 | if (IS_ERR(ptr: host->rst)) { |
483 | dev_err(dev, "%s: failed to get reset control\n" , __func__); |
484 | err = PTR_ERR(ptr: host->rst); |
485 | goto error; |
486 | } |
487 | |
488 | ufs_hisi_set_pm_lvl(hba); |
489 | |
490 | err = ufs_hisi_get_resource(host); |
491 | if (err) |
492 | goto error; |
493 | |
494 | return 0; |
495 | |
496 | error: |
497 | ufshcd_set_variant(hba, NULL); |
498 | return err; |
499 | } |
500 | |
501 | static int ufs_hi3660_init(struct ufs_hba *hba) |
502 | { |
503 | int ret = 0; |
504 | struct device *dev = hba->dev; |
505 | |
506 | ret = ufs_hisi_init_common(hba); |
507 | if (ret) { |
508 | dev_err(dev, "%s: ufs common init fail\n" , __func__); |
509 | return ret; |
510 | } |
511 | |
512 | ufs_hisi_clk_init(hba); |
513 | |
514 | ufs_hisi_soc_init(hba); |
515 | |
516 | return 0; |
517 | } |
518 | |
519 | static int ufs_hi3670_init(struct ufs_hba *hba) |
520 | { |
521 | int ret = 0; |
522 | struct device *dev = hba->dev; |
523 | struct ufs_hisi_host *host; |
524 | |
525 | ret = ufs_hisi_init_common(hba); |
526 | if (ret) { |
527 | dev_err(dev, "%s: ufs common init fail\n" , __func__); |
528 | return ret; |
529 | } |
530 | |
531 | ufs_hisi_clk_init(hba); |
532 | |
533 | ufs_hisi_soc_init(hba); |
534 | |
535 | /* Add cap for 10nm PHY variant on HI3670 SoC */ |
536 | host = ufshcd_get_variant(hba); |
537 | host->caps |= UFS_HISI_CAP_PHY10nm; |
538 | |
539 | return 0; |
540 | } |
541 | |
542 | static const struct ufs_hba_variant_ops ufs_hba_hi3660_vops = { |
543 | .name = "hi3660" , |
544 | .init = ufs_hi3660_init, |
545 | .link_startup_notify = ufs_hisi_link_startup_notify, |
546 | .pwr_change_notify = ufs_hisi_pwr_change_notify, |
547 | .suspend = ufs_hisi_suspend, |
548 | .resume = ufs_hisi_resume, |
549 | }; |
550 | |
551 | static const struct ufs_hba_variant_ops ufs_hba_hi3670_vops = { |
552 | .name = "hi3670" , |
553 | .init = ufs_hi3670_init, |
554 | .link_startup_notify = ufs_hisi_link_startup_notify, |
555 | .pwr_change_notify = ufs_hisi_pwr_change_notify, |
556 | .suspend = ufs_hisi_suspend, |
557 | .resume = ufs_hisi_resume, |
558 | }; |
559 | |
560 | static const struct of_device_id ufs_hisi_of_match[] = { |
561 | { .compatible = "hisilicon,hi3660-ufs" , .data = &ufs_hba_hi3660_vops }, |
562 | { .compatible = "hisilicon,hi3670-ufs" , .data = &ufs_hba_hi3670_vops }, |
563 | {}, |
564 | }; |
565 | |
566 | MODULE_DEVICE_TABLE(of, ufs_hisi_of_match); |
567 | |
568 | static int ufs_hisi_probe(struct platform_device *pdev) |
569 | { |
570 | const struct of_device_id *of_id; |
571 | |
572 | of_id = of_match_node(matches: ufs_hisi_of_match, node: pdev->dev.of_node); |
573 | |
574 | return ufshcd_pltfrm_init(pdev, vops: of_id->data); |
575 | } |
576 | |
577 | static void ufs_hisi_remove(struct platform_device *pdev) |
578 | { |
579 | struct ufs_hba *hba = platform_get_drvdata(pdev); |
580 | |
581 | ufshcd_remove(hba); |
582 | } |
583 | |
584 | static const struct dev_pm_ops ufs_hisi_pm_ops = { |
585 | SET_SYSTEM_SLEEP_PM_OPS(ufshcd_system_suspend, ufshcd_system_resume) |
586 | SET_RUNTIME_PM_OPS(ufshcd_runtime_suspend, ufshcd_runtime_resume, NULL) |
587 | .prepare = ufs_hisi_suspend_prepare, |
588 | .complete = ufshcd_resume_complete, |
589 | }; |
590 | |
591 | static struct platform_driver ufs_hisi_pltform = { |
592 | .probe = ufs_hisi_probe, |
593 | .remove_new = ufs_hisi_remove, |
594 | .driver = { |
595 | .name = "ufshcd-hisi" , |
596 | .pm = &ufs_hisi_pm_ops, |
597 | .of_match_table = ufs_hisi_of_match, |
598 | }, |
599 | }; |
600 | module_platform_driver(ufs_hisi_pltform); |
601 | |
602 | MODULE_LICENSE("GPL" ); |
603 | MODULE_ALIAS("platform:ufshcd-hisi" ); |
604 | MODULE_DESCRIPTION("HiSilicon Hixxxx UFS Driver" ); |
605 | |