1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2017, 2019, The Linux Foundation. All rights reserved. |
4 | */ |
5 | |
6 | #include <linux/clk.h> |
7 | #include <linux/delay.h> |
8 | #include <linux/err.h> |
9 | #include <linux/io.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/mfd/syscon.h> |
12 | #include <linux/module.h> |
13 | #include <linux/nvmem-consumer.h> |
14 | #include <linux/of.h> |
15 | #include <linux/phy/phy.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/regmap.h> |
18 | #include <linux/regulator/consumer.h> |
19 | #include <linux/reset.h> |
20 | #include <linux/slab.h> |
21 | |
22 | #include <dt-bindings/phy/phy-qcom-qusb2.h> |
23 | |
24 | #define QUSB2PHY_PLL 0x0 |
25 | #define QUSB2PHY_PLL_TEST 0x04 |
26 | #define CLK_REF_SEL BIT(7) |
27 | |
28 | #define QUSB2PHY_PLL_TUNE 0x08 |
29 | #define QUSB2PHY_PLL_USER_CTL1 0x0c |
30 | #define QUSB2PHY_PLL_USER_CTL2 0x10 |
31 | #define QUSB2PHY_PLL_AUTOPGM_CTL1 0x1c |
32 | #define QUSB2PHY_PLL_PWR_CTRL 0x18 |
33 | |
34 | /* QUSB2PHY_PLL_STATUS register bits */ |
35 | #define PLL_LOCKED BIT(5) |
36 | |
37 | /* QUSB2PHY_PLL_COMMON_STATUS_ONE register bits */ |
38 | #define CORE_READY_STATUS BIT(0) |
39 | |
40 | /* QUSB2PHY_PORT_POWERDOWN register bits */ |
41 | #define CLAMP_N_EN BIT(5) |
42 | #define FREEZIO_N BIT(1) |
43 | #define POWER_DOWN BIT(0) |
44 | |
45 | /* QUSB2PHY_PWR_CTRL1 register bits */ |
46 | #define PWR_CTRL1_VREF_SUPPLY_TRIM BIT(5) |
47 | #define PWR_CTRL1_CLAMP_N_EN BIT(1) |
48 | |
49 | #define QUSB2PHY_REFCLK_ENABLE BIT(0) |
50 | |
51 | #define PHY_CLK_SCHEME_SEL BIT(0) |
52 | |
53 | /* QUSB2PHY_INTR_CTRL register bits */ |
54 | #define DMSE_INTR_HIGH_SEL BIT(4) |
55 | #define DPSE_INTR_HIGH_SEL BIT(3) |
56 | #define CHG_DET_INTR_EN BIT(2) |
57 | #define DMSE_INTR_EN BIT(1) |
58 | #define DPSE_INTR_EN BIT(0) |
59 | |
60 | /* QUSB2PHY_PLL_CORE_INPUT_OVERRIDE register bits */ |
61 | #define CORE_PLL_EN_FROM_RESET BIT(4) |
62 | #define CORE_RESET BIT(5) |
63 | #define CORE_RESET_MUX BIT(6) |
64 | |
65 | /* QUSB2PHY_IMP_CTRL1 register bits */ |
66 | #define IMP_RES_OFFSET_MASK GENMASK(5, 0) |
67 | #define IMP_RES_OFFSET_SHIFT 0x0 |
68 | |
69 | /* QUSB2PHY_PLL_BIAS_CONTROL_2 register bits */ |
70 | #define BIAS_CTRL2_RES_OFFSET_MASK GENMASK(5, 0) |
71 | #define BIAS_CTRL2_RES_OFFSET_SHIFT 0x0 |
72 | |
73 | /* QUSB2PHY_CHG_CONTROL_2 register bits */ |
74 | #define CHG_CTRL2_OFFSET_MASK GENMASK(5, 4) |
75 | #define CHG_CTRL2_OFFSET_SHIFT 0x4 |
76 | |
77 | /* QUSB2PHY_PORT_TUNE1 register bits */ |
78 | #define HSTX_TRIM_MASK GENMASK(7, 4) |
79 | #define HSTX_TRIM_SHIFT 0x4 |
80 | #define PREEMPH_WIDTH_HALF_BIT BIT(2) |
81 | #define PREEMPHASIS_EN_MASK GENMASK(1, 0) |
82 | #define PREEMPHASIS_EN_SHIFT 0x0 |
83 | |
84 | /* QUSB2PHY_PORT_TUNE2 register bits */ |
85 | #define HSDISC_TRIM_MASK GENMASK(1, 0) |
86 | #define HSDISC_TRIM_SHIFT 0x0 |
87 | |
88 | #define QUSB2PHY_PLL_ANALOG_CONTROLS_TWO 0x04 |
89 | #define QUSB2PHY_PLL_CLOCK_INVERTERS 0x18c |
90 | #define QUSB2PHY_PLL_CMODE 0x2c |
91 | #define QUSB2PHY_PLL_LOCK_DELAY 0x184 |
92 | #define QUSB2PHY_PLL_DIGITAL_TIMERS_TWO 0xb4 |
93 | #define QUSB2PHY_PLL_BIAS_CONTROL_1 0x194 |
94 | #define QUSB2PHY_PLL_BIAS_CONTROL_2 0x198 |
95 | #define QUSB2PHY_PWR_CTRL2 0x214 |
96 | #define QUSB2PHY_IMP_CTRL1 0x220 |
97 | #define QUSB2PHY_IMP_CTRL2 0x224 |
98 | #define QUSB2PHY_CHG_CTRL2 0x23c |
99 | |
100 | struct qusb2_phy_init_tbl { |
101 | unsigned int offset; |
102 | unsigned int val; |
103 | /* |
104 | * register part of layout ? |
105 | * if yes, then offset gives index in the reg-layout |
106 | */ |
107 | int in_layout; |
108 | }; |
109 | |
110 | #define QUSB2_PHY_INIT_CFG(o, v) \ |
111 | { \ |
112 | .offset = o, \ |
113 | .val = v, \ |
114 | } |
115 | |
116 | #define QUSB2_PHY_INIT_CFG_L(o, v) \ |
117 | { \ |
118 | .offset = o, \ |
119 | .val = v, \ |
120 | .in_layout = 1, \ |
121 | } |
122 | |
123 | /* set of registers with offsets different per-PHY */ |
124 | enum qusb2phy_reg_layout { |
125 | QUSB2PHY_PLL_CORE_INPUT_OVERRIDE, |
126 | QUSB2PHY_PLL_STATUS, |
127 | QUSB2PHY_PORT_TUNE1, |
128 | QUSB2PHY_PORT_TUNE2, |
129 | QUSB2PHY_PORT_TUNE3, |
130 | QUSB2PHY_PORT_TUNE4, |
131 | QUSB2PHY_PORT_TUNE5, |
132 | QUSB2PHY_PORT_TEST1, |
133 | QUSB2PHY_PORT_TEST2, |
134 | QUSB2PHY_PORT_POWERDOWN, |
135 | QUSB2PHY_INTR_CTRL, |
136 | }; |
137 | |
138 | static const struct qusb2_phy_init_tbl ipq6018_init_tbl[] = { |
139 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL, 0x14), |
140 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xF8), |
141 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0xB3), |
142 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0x83), |
143 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0xC0), |
144 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30), |
145 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79), |
146 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21), |
147 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE5, 0x00), |
148 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00), |
149 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TEST2, 0x14), |
150 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TEST, 0x80), |
151 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9F), |
152 | }; |
153 | |
154 | static const unsigned int ipq6018_regs_layout[] = { |
155 | [QUSB2PHY_PLL_STATUS] = 0x38, |
156 | [QUSB2PHY_PORT_TUNE1] = 0x80, |
157 | [QUSB2PHY_PORT_TUNE2] = 0x84, |
158 | [QUSB2PHY_PORT_TUNE3] = 0x88, |
159 | [QUSB2PHY_PORT_TUNE4] = 0x8C, |
160 | [QUSB2PHY_PORT_TUNE5] = 0x90, |
161 | [QUSB2PHY_PORT_TEST1] = 0x98, |
162 | [QUSB2PHY_PORT_TEST2] = 0x9C, |
163 | [QUSB2PHY_PORT_POWERDOWN] = 0xB4, |
164 | [QUSB2PHY_INTR_CTRL] = 0xBC, |
165 | }; |
166 | |
167 | static const unsigned int msm8996_regs_layout[] = { |
168 | [QUSB2PHY_PLL_STATUS] = 0x38, |
169 | [QUSB2PHY_PORT_TUNE1] = 0x80, |
170 | [QUSB2PHY_PORT_TUNE2] = 0x84, |
171 | [QUSB2PHY_PORT_TUNE3] = 0x88, |
172 | [QUSB2PHY_PORT_TUNE4] = 0x8c, |
173 | [QUSB2PHY_PORT_TUNE5] = 0x90, |
174 | [QUSB2PHY_PORT_TEST1] = 0xb8, |
175 | [QUSB2PHY_PORT_TEST2] = 0x9c, |
176 | [QUSB2PHY_PORT_POWERDOWN] = 0xb4, |
177 | [QUSB2PHY_INTR_CTRL] = 0xbc, |
178 | }; |
179 | |
180 | static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = { |
181 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xf8), |
182 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0xb3), |
183 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0x83), |
184 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0xc0), |
185 | |
186 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30), |
187 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79), |
188 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21), |
189 | |
190 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TEST2, 0x14), |
191 | |
192 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f), |
193 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00), |
194 | }; |
195 | |
196 | static const unsigned int msm8998_regs_layout[] = { |
197 | [QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8, |
198 | [QUSB2PHY_PLL_STATUS] = 0x1a0, |
199 | [QUSB2PHY_PORT_TUNE1] = 0x23c, |
200 | [QUSB2PHY_PORT_TUNE2] = 0x240, |
201 | [QUSB2PHY_PORT_TUNE3] = 0x244, |
202 | [QUSB2PHY_PORT_TUNE4] = 0x248, |
203 | [QUSB2PHY_PORT_TEST1] = 0x24c, |
204 | [QUSB2PHY_PORT_TEST2] = 0x250, |
205 | [QUSB2PHY_PORT_POWERDOWN] = 0x210, |
206 | [QUSB2PHY_INTR_CTRL] = 0x22c, |
207 | }; |
208 | |
209 | static const struct qusb2_phy_init_tbl msm8998_init_tbl[] = { |
210 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_ANALOG_CONTROLS_TWO, 0x13), |
211 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CLOCK_INVERTERS, 0x7c), |
212 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CMODE, 0x80), |
213 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_LOCK_DELAY, 0x0a), |
214 | |
215 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xa5), |
216 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0x09), |
217 | |
218 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_DIGITAL_TIMERS_TWO, 0x19), |
219 | }; |
220 | |
221 | static const struct qusb2_phy_init_tbl sm6115_init_tbl[] = { |
222 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xf8), |
223 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0x53), |
224 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0x81), |
225 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0x17), |
226 | |
227 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30), |
228 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79), |
229 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21), |
230 | |
231 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TEST2, 0x14), |
232 | |
233 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f), |
234 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00), |
235 | }; |
236 | |
237 | static const unsigned int qusb2_v2_regs_layout[] = { |
238 | [QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8, |
239 | [QUSB2PHY_PLL_STATUS] = 0x1a0, |
240 | [QUSB2PHY_PORT_TUNE1] = 0x240, |
241 | [QUSB2PHY_PORT_TUNE2] = 0x244, |
242 | [QUSB2PHY_PORT_TUNE3] = 0x248, |
243 | [QUSB2PHY_PORT_TUNE4] = 0x24c, |
244 | [QUSB2PHY_PORT_TUNE5] = 0x250, |
245 | [QUSB2PHY_PORT_TEST1] = 0x254, |
246 | [QUSB2PHY_PORT_TEST2] = 0x258, |
247 | [QUSB2PHY_PORT_POWERDOWN] = 0x210, |
248 | [QUSB2PHY_INTR_CTRL] = 0x230, |
249 | }; |
250 | |
251 | static const struct qusb2_phy_init_tbl qusb2_v2_init_tbl[] = { |
252 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_ANALOG_CONTROLS_TWO, 0x03), |
253 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CLOCK_INVERTERS, 0x7c), |
254 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CMODE, 0x80), |
255 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_LOCK_DELAY, 0x0a), |
256 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_DIGITAL_TIMERS_TWO, 0x19), |
257 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_BIAS_CONTROL_1, 0x40), |
258 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_BIAS_CONTROL_2, 0x20), |
259 | QUSB2_PHY_INIT_CFG(QUSB2PHY_PWR_CTRL2, 0x21), |
260 | QUSB2_PHY_INIT_CFG(QUSB2PHY_IMP_CTRL1, 0x0), |
261 | QUSB2_PHY_INIT_CFG(QUSB2PHY_IMP_CTRL2, 0x58), |
262 | |
263 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0x30), |
264 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0x29), |
265 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0xca), |
266 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0x04), |
267 | QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE5, 0x03), |
268 | |
269 | QUSB2_PHY_INIT_CFG(QUSB2PHY_CHG_CTRL2, 0x0), |
270 | }; |
271 | |
272 | struct qusb2_phy_cfg { |
273 | const struct qusb2_phy_init_tbl *tbl; |
274 | /* number of entries in the table */ |
275 | unsigned int tbl_num; |
276 | /* offset to PHY_CLK_SCHEME register in TCSR map */ |
277 | unsigned int clk_scheme_offset; |
278 | |
279 | /* array of registers with different offsets */ |
280 | const unsigned int *regs; |
281 | unsigned int mask_core_ready; |
282 | unsigned int disable_ctrl; |
283 | unsigned int autoresume_en; |
284 | |
285 | /* true if PHY has PLL_TEST register to select clk_scheme */ |
286 | bool has_pll_test; |
287 | |
288 | /* true if TUNE1 register must be updated by fused value, else TUNE2 */ |
289 | bool update_tune1_with_efuse; |
290 | |
291 | /* true if PHY has PLL_CORE_INPUT_OVERRIDE register to reset PLL */ |
292 | bool has_pll_override; |
293 | |
294 | /* true if PHY default clk scheme is single-ended */ |
295 | bool se_clk_scheme_default; |
296 | }; |
297 | |
298 | static const struct qusb2_phy_cfg msm8996_phy_cfg = { |
299 | .tbl = msm8996_init_tbl, |
300 | .tbl_num = ARRAY_SIZE(msm8996_init_tbl), |
301 | .regs = msm8996_regs_layout, |
302 | |
303 | .has_pll_test = true, |
304 | .se_clk_scheme_default = true, |
305 | .disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN), |
306 | .mask_core_ready = PLL_LOCKED, |
307 | .autoresume_en = BIT(3), |
308 | }; |
309 | |
310 | static const struct qusb2_phy_cfg msm8998_phy_cfg = { |
311 | .tbl = msm8998_init_tbl, |
312 | .tbl_num = ARRAY_SIZE(msm8998_init_tbl), |
313 | .regs = msm8998_regs_layout, |
314 | |
315 | .disable_ctrl = POWER_DOWN, |
316 | .mask_core_ready = CORE_READY_STATUS, |
317 | .has_pll_override = true, |
318 | .se_clk_scheme_default = true, |
319 | .autoresume_en = BIT(0), |
320 | .update_tune1_with_efuse = true, |
321 | }; |
322 | |
323 | static const struct qusb2_phy_cfg ipq6018_phy_cfg = { |
324 | .tbl = ipq6018_init_tbl, |
325 | .tbl_num = ARRAY_SIZE(ipq6018_init_tbl), |
326 | .regs = ipq6018_regs_layout, |
327 | |
328 | .disable_ctrl = POWER_DOWN, |
329 | .mask_core_ready = PLL_LOCKED, |
330 | /* autoresume not used */ |
331 | .autoresume_en = BIT(0), |
332 | }; |
333 | |
334 | static const struct qusb2_phy_cfg qusb2_v2_phy_cfg = { |
335 | .tbl = qusb2_v2_init_tbl, |
336 | .tbl_num = ARRAY_SIZE(qusb2_v2_init_tbl), |
337 | .regs = qusb2_v2_regs_layout, |
338 | |
339 | .disable_ctrl = (PWR_CTRL1_VREF_SUPPLY_TRIM | PWR_CTRL1_CLAMP_N_EN | |
340 | POWER_DOWN), |
341 | .mask_core_ready = CORE_READY_STATUS, |
342 | .has_pll_override = true, |
343 | .se_clk_scheme_default = true, |
344 | .autoresume_en = BIT(0), |
345 | .update_tune1_with_efuse = true, |
346 | }; |
347 | |
348 | static const struct qusb2_phy_cfg sdm660_phy_cfg = { |
349 | .tbl = msm8996_init_tbl, |
350 | .tbl_num = ARRAY_SIZE(msm8996_init_tbl), |
351 | .regs = msm8996_regs_layout, |
352 | |
353 | .has_pll_test = true, |
354 | .se_clk_scheme_default = false, |
355 | .disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN), |
356 | .mask_core_ready = PLL_LOCKED, |
357 | .autoresume_en = BIT(3), |
358 | }; |
359 | |
360 | static const struct qusb2_phy_cfg sm6115_phy_cfg = { |
361 | .tbl = sm6115_init_tbl, |
362 | .tbl_num = ARRAY_SIZE(sm6115_init_tbl), |
363 | .regs = msm8996_regs_layout, |
364 | |
365 | .has_pll_test = true, |
366 | .se_clk_scheme_default = true, |
367 | .disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN), |
368 | .mask_core_ready = PLL_LOCKED, |
369 | .autoresume_en = BIT(3), |
370 | }; |
371 | |
372 | static const char * const qusb2_phy_vreg_names[] = { |
373 | "vdd" , "vdda-pll" , "vdda-phy-dpdm" , |
374 | }; |
375 | |
376 | #define QUSB2_NUM_VREGS ARRAY_SIZE(qusb2_phy_vreg_names) |
377 | |
378 | /* struct override_param - structure holding qusb2 v2 phy overriding param |
379 | * set override true if the device tree property exists and read and assign |
380 | * to value |
381 | */ |
382 | struct override_param { |
383 | bool override; |
384 | u8 value; |
385 | }; |
386 | |
387 | /*struct override_params - structure holding qusb2 v2 phy overriding params |
388 | * @imp_res_offset: rescode offset to be updated in IMP_CTRL1 register |
389 | * @hstx_trim: HSTX_TRIM to be updated in TUNE1 register |
390 | * @preemphasis: Amplitude Pre-Emphasis to be updated in TUNE1 register |
391 | * @preemphasis_width: half/full-width Pre-Emphasis updated via TUNE1 |
392 | * @bias_ctrl: bias ctrl to be updated in BIAS_CONTROL_2 register |
393 | * @charge_ctrl: charge ctrl to be updated in CHG_CTRL2 register |
394 | * @hsdisc_trim: disconnect threshold to be updated in TUNE2 register |
395 | */ |
396 | struct override_params { |
397 | struct override_param imp_res_offset; |
398 | struct override_param hstx_trim; |
399 | struct override_param preemphasis; |
400 | struct override_param preemphasis_width; |
401 | struct override_param bias_ctrl; |
402 | struct override_param charge_ctrl; |
403 | struct override_param hsdisc_trim; |
404 | }; |
405 | |
406 | /** |
407 | * struct qusb2_phy - structure holding qusb2 phy attributes |
408 | * |
409 | * @phy: generic phy |
410 | * @base: iomapped memory space for qubs2 phy |
411 | * |
412 | * @cfg_ahb_clk: AHB2PHY interface clock |
413 | * @ref_clk: phy reference clock |
414 | * @iface_clk: phy interface clock |
415 | * @phy_reset: phy reset control |
416 | * @vregs: regulator supplies bulk data |
417 | * |
418 | * @tcsr: TCSR syscon register map |
419 | * @cell: nvmem cell containing phy tuning value |
420 | * |
421 | * @overrides: pointer to structure for all overriding tuning params |
422 | * |
423 | * @cfg: phy config data |
424 | * @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme |
425 | * @phy_initialized: indicate if PHY has been initialized |
426 | * @mode: current PHY mode |
427 | */ |
428 | struct qusb2_phy { |
429 | struct phy *phy; |
430 | void __iomem *base; |
431 | |
432 | struct clk *cfg_ahb_clk; |
433 | struct clk *ref_clk; |
434 | struct clk *iface_clk; |
435 | struct reset_control *phy_reset; |
436 | struct regulator_bulk_data vregs[QUSB2_NUM_VREGS]; |
437 | |
438 | struct regmap *tcsr; |
439 | struct nvmem_cell *cell; |
440 | |
441 | struct override_params overrides; |
442 | |
443 | const struct qusb2_phy_cfg *cfg; |
444 | bool has_se_clk_scheme; |
445 | bool phy_initialized; |
446 | enum phy_mode mode; |
447 | }; |
448 | |
449 | static inline void qusb2_write_mask(void __iomem *base, u32 offset, |
450 | u32 val, u32 mask) |
451 | { |
452 | u32 reg; |
453 | |
454 | reg = readl(addr: base + offset); |
455 | reg &= ~mask; |
456 | reg |= val & mask; |
457 | writel(val: reg, addr: base + offset); |
458 | |
459 | /* Ensure above write is completed */ |
460 | readl(addr: base + offset); |
461 | } |
462 | |
463 | static inline void qusb2_setbits(void __iomem *base, u32 offset, u32 val) |
464 | { |
465 | u32 reg; |
466 | |
467 | reg = readl(addr: base + offset); |
468 | reg |= val; |
469 | writel(val: reg, addr: base + offset); |
470 | |
471 | /* Ensure above write is completed */ |
472 | readl(addr: base + offset); |
473 | } |
474 | |
475 | static inline void qusb2_clrbits(void __iomem *base, u32 offset, u32 val) |
476 | { |
477 | u32 reg; |
478 | |
479 | reg = readl(addr: base + offset); |
480 | reg &= ~val; |
481 | writel(val: reg, addr: base + offset); |
482 | |
483 | /* Ensure above write is completed */ |
484 | readl(addr: base + offset); |
485 | } |
486 | |
487 | static inline |
488 | void qcom_qusb2_phy_configure(void __iomem *base, |
489 | const unsigned int *regs, |
490 | const struct qusb2_phy_init_tbl tbl[], int num) |
491 | { |
492 | int i; |
493 | |
494 | for (i = 0; i < num; i++) { |
495 | if (tbl[i].in_layout) |
496 | writel(val: tbl[i].val, addr: base + regs[tbl[i].offset]); |
497 | else |
498 | writel(val: tbl[i].val, addr: base + tbl[i].offset); |
499 | } |
500 | } |
501 | |
502 | /* |
503 | * Update board specific PHY tuning override values if specified from |
504 | * device tree. |
505 | */ |
506 | static void qusb2_phy_override_phy_params(struct qusb2_phy *qphy) |
507 | { |
508 | const struct qusb2_phy_cfg *cfg = qphy->cfg; |
509 | struct override_params *or = &qphy->overrides; |
510 | |
511 | if (or->imp_res_offset.override) |
512 | qusb2_write_mask(base: qphy->base, QUSB2PHY_IMP_CTRL1, |
513 | val: or->imp_res_offset.value << IMP_RES_OFFSET_SHIFT, |
514 | IMP_RES_OFFSET_MASK); |
515 | |
516 | if (or->bias_ctrl.override) |
517 | qusb2_write_mask(base: qphy->base, QUSB2PHY_PLL_BIAS_CONTROL_2, |
518 | val: or->bias_ctrl.value << BIAS_CTRL2_RES_OFFSET_SHIFT, |
519 | BIAS_CTRL2_RES_OFFSET_MASK); |
520 | |
521 | if (or->charge_ctrl.override) |
522 | qusb2_write_mask(base: qphy->base, QUSB2PHY_CHG_CTRL2, |
523 | val: or->charge_ctrl.value << CHG_CTRL2_OFFSET_SHIFT, |
524 | CHG_CTRL2_OFFSET_MASK); |
525 | |
526 | if (or->hstx_trim.override) |
527 | qusb2_write_mask(base: qphy->base, offset: cfg->regs[QUSB2PHY_PORT_TUNE1], |
528 | val: or->hstx_trim.value << HSTX_TRIM_SHIFT, |
529 | HSTX_TRIM_MASK); |
530 | |
531 | if (or->preemphasis.override) |
532 | qusb2_write_mask(base: qphy->base, offset: cfg->regs[QUSB2PHY_PORT_TUNE1], |
533 | val: or->preemphasis.value << PREEMPHASIS_EN_SHIFT, |
534 | PREEMPHASIS_EN_MASK); |
535 | |
536 | if (or->preemphasis_width.override) { |
537 | if (or->preemphasis_width.value == |
538 | QUSB2_V2_PREEMPHASIS_WIDTH_HALF_BIT) |
539 | qusb2_setbits(base: qphy->base, |
540 | offset: cfg->regs[QUSB2PHY_PORT_TUNE1], |
541 | PREEMPH_WIDTH_HALF_BIT); |
542 | else |
543 | qusb2_clrbits(base: qphy->base, |
544 | offset: cfg->regs[QUSB2PHY_PORT_TUNE1], |
545 | PREEMPH_WIDTH_HALF_BIT); |
546 | } |
547 | |
548 | if (or->hsdisc_trim.override) |
549 | qusb2_write_mask(base: qphy->base, offset: cfg->regs[QUSB2PHY_PORT_TUNE2], |
550 | val: or->hsdisc_trim.value << HSDISC_TRIM_SHIFT, |
551 | HSDISC_TRIM_MASK); |
552 | } |
553 | |
554 | /* |
555 | * Fetches HS Tx tuning value from nvmem and sets the |
556 | * QUSB2PHY_PORT_TUNE1/2 register. |
557 | * For error case, skip setting the value and use the default value. |
558 | */ |
559 | static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy) |
560 | { |
561 | struct device *dev = &qphy->phy->dev; |
562 | const struct qusb2_phy_cfg *cfg = qphy->cfg; |
563 | u8 *val, hstx_trim; |
564 | |
565 | /* efuse register is optional */ |
566 | if (!qphy->cell) |
567 | return; |
568 | |
569 | /* |
570 | * Read efuse register having TUNE2/1 parameter's high nibble. |
571 | * If efuse register shows value as 0x0 (indicating value is not |
572 | * fused), or if we fail to find a valid efuse register setting, |
573 | * then use default value for high nibble that we have already |
574 | * set while configuring the phy. |
575 | */ |
576 | val = nvmem_cell_read(cell: qphy->cell, NULL); |
577 | if (IS_ERR(ptr: val)) { |
578 | dev_dbg(dev, "failed to read a valid hs-tx trim value\n" ); |
579 | return; |
580 | } |
581 | hstx_trim = val[0]; |
582 | kfree(objp: val); |
583 | if (!hstx_trim) { |
584 | dev_dbg(dev, "failed to read a valid hs-tx trim value\n" ); |
585 | return; |
586 | } |
587 | |
588 | /* Fused TUNE1/2 value is the higher nibble only */ |
589 | if (cfg->update_tune1_with_efuse) |
590 | qusb2_write_mask(base: qphy->base, offset: cfg->regs[QUSB2PHY_PORT_TUNE1], |
591 | val: hstx_trim << HSTX_TRIM_SHIFT, HSTX_TRIM_MASK); |
592 | else |
593 | qusb2_write_mask(base: qphy->base, offset: cfg->regs[QUSB2PHY_PORT_TUNE2], |
594 | val: hstx_trim << HSTX_TRIM_SHIFT, HSTX_TRIM_MASK); |
595 | } |
596 | |
597 | static int qusb2_phy_set_mode(struct phy *phy, |
598 | enum phy_mode mode, int submode) |
599 | { |
600 | struct qusb2_phy *qphy = phy_get_drvdata(phy); |
601 | |
602 | qphy->mode = mode; |
603 | |
604 | return 0; |
605 | } |
606 | |
607 | static int __maybe_unused qusb2_phy_runtime_suspend(struct device *dev) |
608 | { |
609 | struct qusb2_phy *qphy = dev_get_drvdata(dev); |
610 | const struct qusb2_phy_cfg *cfg = qphy->cfg; |
611 | u32 intr_mask; |
612 | |
613 | dev_vdbg(dev, "Suspending QUSB2 Phy, mode:%d\n" , qphy->mode); |
614 | |
615 | if (!qphy->phy_initialized) { |
616 | dev_vdbg(dev, "PHY not initialized, bailing out\n" ); |
617 | return 0; |
618 | } |
619 | |
620 | /* |
621 | * Enable DP/DM interrupts to detect line state changes based on current |
622 | * speed. In other words, enable the triggers _opposite_ of what the |
623 | * current D+/D- levels are e.g. if currently D+ high, D- low |
624 | * (HS 'J'/Suspend), configure the mask to trigger on D+ low OR D- high |
625 | */ |
626 | intr_mask = DPSE_INTR_EN | DMSE_INTR_EN; |
627 | switch (qphy->mode) { |
628 | case PHY_MODE_USB_HOST_HS: |
629 | case PHY_MODE_USB_HOST_FS: |
630 | case PHY_MODE_USB_DEVICE_HS: |
631 | case PHY_MODE_USB_DEVICE_FS: |
632 | intr_mask |= DMSE_INTR_HIGH_SEL; |
633 | break; |
634 | case PHY_MODE_USB_HOST_LS: |
635 | case PHY_MODE_USB_DEVICE_LS: |
636 | intr_mask |= DPSE_INTR_HIGH_SEL; |
637 | break; |
638 | default: |
639 | /* No device connected, enable both DP/DM high interrupt */ |
640 | intr_mask |= DMSE_INTR_HIGH_SEL; |
641 | intr_mask |= DPSE_INTR_HIGH_SEL; |
642 | break; |
643 | } |
644 | |
645 | writel(val: intr_mask, addr: qphy->base + cfg->regs[QUSB2PHY_INTR_CTRL]); |
646 | |
647 | /* hold core PLL into reset */ |
648 | if (cfg->has_pll_override) { |
649 | qusb2_setbits(base: qphy->base, |
650 | offset: cfg->regs[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE], |
651 | CORE_PLL_EN_FROM_RESET | CORE_RESET | |
652 | CORE_RESET_MUX); |
653 | } |
654 | |
655 | /* enable phy auto-resume only if device is connected on bus */ |
656 | if (qphy->mode != PHY_MODE_INVALID) { |
657 | qusb2_setbits(base: qphy->base, offset: cfg->regs[QUSB2PHY_PORT_TEST1], |
658 | val: cfg->autoresume_en); |
659 | /* Autoresume bit has to be toggled in order to enable it */ |
660 | qusb2_clrbits(base: qphy->base, offset: cfg->regs[QUSB2PHY_PORT_TEST1], |
661 | val: cfg->autoresume_en); |
662 | } |
663 | |
664 | if (!qphy->has_se_clk_scheme) |
665 | clk_disable_unprepare(clk: qphy->ref_clk); |
666 | |
667 | clk_disable_unprepare(clk: qphy->cfg_ahb_clk); |
668 | clk_disable_unprepare(clk: qphy->iface_clk); |
669 | |
670 | return 0; |
671 | } |
672 | |
673 | static int __maybe_unused qusb2_phy_runtime_resume(struct device *dev) |
674 | { |
675 | struct qusb2_phy *qphy = dev_get_drvdata(dev); |
676 | const struct qusb2_phy_cfg *cfg = qphy->cfg; |
677 | int ret; |
678 | |
679 | dev_vdbg(dev, "Resuming QUSB2 phy, mode:%d\n" , qphy->mode); |
680 | |
681 | if (!qphy->phy_initialized) { |
682 | dev_vdbg(dev, "PHY not initialized, bailing out\n" ); |
683 | return 0; |
684 | } |
685 | |
686 | ret = clk_prepare_enable(clk: qphy->iface_clk); |
687 | if (ret) { |
688 | dev_err(dev, "failed to enable iface_clk, %d\n" , ret); |
689 | return ret; |
690 | } |
691 | |
692 | ret = clk_prepare_enable(clk: qphy->cfg_ahb_clk); |
693 | if (ret) { |
694 | dev_err(dev, "failed to enable cfg ahb clock, %d\n" , ret); |
695 | goto disable_iface_clk; |
696 | } |
697 | |
698 | if (!qphy->has_se_clk_scheme) { |
699 | ret = clk_prepare_enable(clk: qphy->ref_clk); |
700 | if (ret) { |
701 | dev_err(dev, "failed to enable ref clk, %d\n" , ret); |
702 | goto disable_ahb_clk; |
703 | } |
704 | } |
705 | |
706 | writel(val: 0x0, addr: qphy->base + cfg->regs[QUSB2PHY_INTR_CTRL]); |
707 | |
708 | /* bring core PLL out of reset */ |
709 | if (cfg->has_pll_override) { |
710 | qusb2_clrbits(base: qphy->base, |
711 | offset: cfg->regs[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE], |
712 | CORE_RESET | CORE_RESET_MUX); |
713 | } |
714 | |
715 | return 0; |
716 | |
717 | disable_ahb_clk: |
718 | clk_disable_unprepare(clk: qphy->cfg_ahb_clk); |
719 | disable_iface_clk: |
720 | clk_disable_unprepare(clk: qphy->iface_clk); |
721 | |
722 | return ret; |
723 | } |
724 | |
725 | static int qusb2_phy_init(struct phy *phy) |
726 | { |
727 | struct qusb2_phy *qphy = phy_get_drvdata(phy); |
728 | const struct qusb2_phy_cfg *cfg = qphy->cfg; |
729 | unsigned int val = 0; |
730 | unsigned int clk_scheme; |
731 | int ret; |
732 | |
733 | dev_vdbg(&phy->dev, "%s(): Initializing QUSB2 phy\n" , __func__); |
734 | |
735 | /* turn on regulator supplies */ |
736 | ret = regulator_bulk_enable(ARRAY_SIZE(qphy->vregs), consumers: qphy->vregs); |
737 | if (ret) |
738 | return ret; |
739 | |
740 | ret = clk_prepare_enable(clk: qphy->iface_clk); |
741 | if (ret) { |
742 | dev_err(&phy->dev, "failed to enable iface_clk, %d\n" , ret); |
743 | goto poweroff_phy; |
744 | } |
745 | |
746 | /* enable ahb interface clock to program phy */ |
747 | ret = clk_prepare_enable(clk: qphy->cfg_ahb_clk); |
748 | if (ret) { |
749 | dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n" , ret); |
750 | goto disable_iface_clk; |
751 | } |
752 | |
753 | /* Perform phy reset */ |
754 | ret = reset_control_assert(rstc: qphy->phy_reset); |
755 | if (ret) { |
756 | dev_err(&phy->dev, "failed to assert phy_reset, %d\n" , ret); |
757 | goto disable_ahb_clk; |
758 | } |
759 | |
760 | /* 100 us delay to keep PHY in reset mode */ |
761 | usleep_range(min: 100, max: 150); |
762 | |
763 | ret = reset_control_deassert(rstc: qphy->phy_reset); |
764 | if (ret) { |
765 | dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n" , ret); |
766 | goto disable_ahb_clk; |
767 | } |
768 | |
769 | /* Disable the PHY */ |
770 | qusb2_setbits(base: qphy->base, offset: cfg->regs[QUSB2PHY_PORT_POWERDOWN], |
771 | val: qphy->cfg->disable_ctrl); |
772 | |
773 | if (cfg->has_pll_test) { |
774 | /* save reset value to override reference clock scheme later */ |
775 | val = readl(addr: qphy->base + QUSB2PHY_PLL_TEST); |
776 | } |
777 | |
778 | qcom_qusb2_phy_configure(base: qphy->base, regs: cfg->regs, tbl: cfg->tbl, |
779 | num: cfg->tbl_num); |
780 | |
781 | /* Override board specific PHY tuning values */ |
782 | qusb2_phy_override_phy_params(qphy); |
783 | |
784 | /* Set efuse value for tuning the PHY */ |
785 | qusb2_phy_set_tune2_param(qphy); |
786 | |
787 | /* Enable the PHY */ |
788 | qusb2_clrbits(base: qphy->base, offset: cfg->regs[QUSB2PHY_PORT_POWERDOWN], |
789 | POWER_DOWN); |
790 | |
791 | /* Required to get phy pll lock successfully */ |
792 | usleep_range(min: 150, max: 160); |
793 | |
794 | /* |
795 | * Not all the SoCs have got a readable TCSR_PHY_CLK_SCHEME |
796 | * register in the TCSR so, if there's none, use the default |
797 | * value hardcoded in the configuration. |
798 | */ |
799 | qphy->has_se_clk_scheme = cfg->se_clk_scheme_default; |
800 | |
801 | /* |
802 | * read TCSR_PHY_CLK_SCHEME register to check if single-ended |
803 | * clock scheme is selected. If yes, then disable differential |
804 | * ref_clk and use single-ended clock, otherwise use differential |
805 | * ref_clk only. |
806 | */ |
807 | if (qphy->tcsr) { |
808 | ret = regmap_read(map: qphy->tcsr, reg: qphy->cfg->clk_scheme_offset, |
809 | val: &clk_scheme); |
810 | if (ret) { |
811 | dev_err(&phy->dev, "failed to read clk scheme reg\n" ); |
812 | goto assert_phy_reset; |
813 | } |
814 | |
815 | /* is it a differential clock scheme ? */ |
816 | if (!(clk_scheme & PHY_CLK_SCHEME_SEL)) { |
817 | dev_vdbg(&phy->dev, "%s(): select differential clk\n" , |
818 | __func__); |
819 | qphy->has_se_clk_scheme = false; |
820 | } else { |
821 | dev_vdbg(&phy->dev, "%s(): select single-ended clk\n" , |
822 | __func__); |
823 | } |
824 | } |
825 | |
826 | if (!qphy->has_se_clk_scheme) { |
827 | ret = clk_prepare_enable(clk: qphy->ref_clk); |
828 | if (ret) { |
829 | dev_err(&phy->dev, "failed to enable ref clk, %d\n" , |
830 | ret); |
831 | goto assert_phy_reset; |
832 | } |
833 | } |
834 | |
835 | if (cfg->has_pll_test) { |
836 | if (!qphy->has_se_clk_scheme) |
837 | val &= ~CLK_REF_SEL; |
838 | else |
839 | val |= CLK_REF_SEL; |
840 | |
841 | writel(val, addr: qphy->base + QUSB2PHY_PLL_TEST); |
842 | |
843 | /* ensure above write is through */ |
844 | readl(addr: qphy->base + QUSB2PHY_PLL_TEST); |
845 | } |
846 | |
847 | /* Required to get phy pll lock successfully */ |
848 | usleep_range(min: 100, max: 110); |
849 | |
850 | val = readb(addr: qphy->base + cfg->regs[QUSB2PHY_PLL_STATUS]); |
851 | if (!(val & cfg->mask_core_ready)) { |
852 | dev_err(&phy->dev, |
853 | "QUSB2PHY pll lock failed: status reg = %x\n" , val); |
854 | ret = -EBUSY; |
855 | goto disable_ref_clk; |
856 | } |
857 | qphy->phy_initialized = true; |
858 | |
859 | return 0; |
860 | |
861 | disable_ref_clk: |
862 | if (!qphy->has_se_clk_scheme) |
863 | clk_disable_unprepare(clk: qphy->ref_clk); |
864 | assert_phy_reset: |
865 | reset_control_assert(rstc: qphy->phy_reset); |
866 | disable_ahb_clk: |
867 | clk_disable_unprepare(clk: qphy->cfg_ahb_clk); |
868 | disable_iface_clk: |
869 | clk_disable_unprepare(clk: qphy->iface_clk); |
870 | poweroff_phy: |
871 | regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), consumers: qphy->vregs); |
872 | |
873 | return ret; |
874 | } |
875 | |
876 | static int qusb2_phy_exit(struct phy *phy) |
877 | { |
878 | struct qusb2_phy *qphy = phy_get_drvdata(phy); |
879 | |
880 | /* Disable the PHY */ |
881 | qusb2_setbits(base: qphy->base, offset: qphy->cfg->regs[QUSB2PHY_PORT_POWERDOWN], |
882 | val: qphy->cfg->disable_ctrl); |
883 | |
884 | if (!qphy->has_se_clk_scheme) |
885 | clk_disable_unprepare(clk: qphy->ref_clk); |
886 | |
887 | reset_control_assert(rstc: qphy->phy_reset); |
888 | |
889 | clk_disable_unprepare(clk: qphy->cfg_ahb_clk); |
890 | clk_disable_unprepare(clk: qphy->iface_clk); |
891 | |
892 | regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), consumers: qphy->vregs); |
893 | |
894 | qphy->phy_initialized = false; |
895 | |
896 | return 0; |
897 | } |
898 | |
899 | static const struct phy_ops qusb2_phy_gen_ops = { |
900 | .init = qusb2_phy_init, |
901 | .exit = qusb2_phy_exit, |
902 | .set_mode = qusb2_phy_set_mode, |
903 | .owner = THIS_MODULE, |
904 | }; |
905 | |
906 | static const struct of_device_id qusb2_phy_of_match_table[] = { |
907 | { |
908 | .compatible = "qcom,ipq6018-qusb2-phy" , |
909 | .data = &ipq6018_phy_cfg, |
910 | }, { |
911 | .compatible = "qcom,ipq8074-qusb2-phy" , |
912 | .data = &msm8996_phy_cfg, |
913 | }, { |
914 | .compatible = "qcom,ipq9574-qusb2-phy" , |
915 | .data = &ipq6018_phy_cfg, |
916 | }, { |
917 | .compatible = "qcom,msm8953-qusb2-phy" , |
918 | .data = &msm8996_phy_cfg, |
919 | }, { |
920 | .compatible = "qcom,msm8996-qusb2-phy" , |
921 | .data = &msm8996_phy_cfg, |
922 | }, { |
923 | .compatible = "qcom,msm8998-qusb2-phy" , |
924 | .data = &msm8998_phy_cfg, |
925 | }, { |
926 | .compatible = "qcom,qcm2290-qusb2-phy" , |
927 | .data = &sm6115_phy_cfg, |
928 | }, { |
929 | .compatible = "qcom,sdm660-qusb2-phy" , |
930 | .data = &sdm660_phy_cfg, |
931 | }, { |
932 | .compatible = "qcom,sm4250-qusb2-phy" , |
933 | .data = &sm6115_phy_cfg, |
934 | }, { |
935 | .compatible = "qcom,sm6115-qusb2-phy" , |
936 | .data = &sm6115_phy_cfg, |
937 | }, { |
938 | /* |
939 | * Deprecated. Only here to support legacy device |
940 | * trees that didn't include "qcom,qusb2-v2-phy" |
941 | */ |
942 | .compatible = "qcom,sdm845-qusb2-phy" , |
943 | .data = &qusb2_v2_phy_cfg, |
944 | }, { |
945 | .compatible = "qcom,qusb2-v2-phy" , |
946 | .data = &qusb2_v2_phy_cfg, |
947 | }, |
948 | { }, |
949 | }; |
950 | MODULE_DEVICE_TABLE(of, qusb2_phy_of_match_table); |
951 | |
952 | static const struct dev_pm_ops qusb2_phy_pm_ops = { |
953 | SET_RUNTIME_PM_OPS(qusb2_phy_runtime_suspend, |
954 | qusb2_phy_runtime_resume, NULL) |
955 | }; |
956 | |
957 | static int qusb2_phy_probe(struct platform_device *pdev) |
958 | { |
959 | struct device *dev = &pdev->dev; |
960 | struct qusb2_phy *qphy; |
961 | struct phy_provider *phy_provider; |
962 | struct phy *generic_phy; |
963 | int ret, i; |
964 | int num; |
965 | u32 value; |
966 | struct override_params *or; |
967 | |
968 | qphy = devm_kzalloc(dev, size: sizeof(*qphy), GFP_KERNEL); |
969 | if (!qphy) |
970 | return -ENOMEM; |
971 | or = &qphy->overrides; |
972 | |
973 | qphy->base = devm_platform_ioremap_resource(pdev, index: 0); |
974 | if (IS_ERR(ptr: qphy->base)) |
975 | return PTR_ERR(ptr: qphy->base); |
976 | |
977 | qphy->cfg_ahb_clk = devm_clk_get(dev, id: "cfg_ahb" ); |
978 | if (IS_ERR(ptr: qphy->cfg_ahb_clk)) |
979 | return dev_err_probe(dev, err: PTR_ERR(ptr: qphy->cfg_ahb_clk), |
980 | fmt: "failed to get cfg ahb clk\n" ); |
981 | |
982 | qphy->ref_clk = devm_clk_get(dev, id: "ref" ); |
983 | if (IS_ERR(ptr: qphy->ref_clk)) |
984 | return dev_err_probe(dev, err: PTR_ERR(ptr: qphy->ref_clk), |
985 | fmt: "failed to get ref clk\n" ); |
986 | |
987 | qphy->iface_clk = devm_clk_get_optional(dev, id: "iface" ); |
988 | if (IS_ERR(ptr: qphy->iface_clk)) |
989 | return PTR_ERR(ptr: qphy->iface_clk); |
990 | |
991 | qphy->phy_reset = devm_reset_control_get_by_index(dev: &pdev->dev, index: 0); |
992 | if (IS_ERR(ptr: qphy->phy_reset)) { |
993 | dev_err(dev, "failed to get phy core reset\n" ); |
994 | return PTR_ERR(ptr: qphy->phy_reset); |
995 | } |
996 | |
997 | num = ARRAY_SIZE(qphy->vregs); |
998 | for (i = 0; i < num; i++) |
999 | qphy->vregs[i].supply = qusb2_phy_vreg_names[i]; |
1000 | |
1001 | ret = devm_regulator_bulk_get(dev, num_consumers: num, consumers: qphy->vregs); |
1002 | if (ret) |
1003 | return dev_err_probe(dev, err: ret, |
1004 | fmt: "failed to get regulator supplies\n" ); |
1005 | |
1006 | /* Get the specific init parameters of QMP phy */ |
1007 | qphy->cfg = of_device_get_match_data(dev); |
1008 | |
1009 | qphy->tcsr = syscon_regmap_lookup_by_phandle(np: dev->of_node, |
1010 | property: "qcom,tcsr-syscon" ); |
1011 | if (IS_ERR(ptr: qphy->tcsr)) { |
1012 | dev_dbg(dev, "failed to lookup TCSR regmap\n" ); |
1013 | qphy->tcsr = NULL; |
1014 | } |
1015 | |
1016 | qphy->cell = devm_nvmem_cell_get(dev, NULL); |
1017 | if (IS_ERR(ptr: qphy->cell)) { |
1018 | if (PTR_ERR(ptr: qphy->cell) == -EPROBE_DEFER) |
1019 | return -EPROBE_DEFER; |
1020 | qphy->cell = NULL; |
1021 | dev_dbg(dev, "failed to lookup tune2 hstx trim value\n" ); |
1022 | } |
1023 | |
1024 | if (!of_property_read_u32(np: dev->of_node, propname: "qcom,imp-res-offset-value" , |
1025 | out_value: &value)) { |
1026 | or->imp_res_offset.value = (u8)value; |
1027 | or->imp_res_offset.override = true; |
1028 | } |
1029 | |
1030 | if (!of_property_read_u32(np: dev->of_node, propname: "qcom,bias-ctrl-value" , |
1031 | out_value: &value)) { |
1032 | or->bias_ctrl.value = (u8)value; |
1033 | or->bias_ctrl.override = true; |
1034 | } |
1035 | |
1036 | if (!of_property_read_u32(np: dev->of_node, propname: "qcom,charge-ctrl-value" , |
1037 | out_value: &value)) { |
1038 | or->charge_ctrl.value = (u8)value; |
1039 | or->charge_ctrl.override = true; |
1040 | } |
1041 | |
1042 | if (!of_property_read_u32(np: dev->of_node, propname: "qcom,hstx-trim-value" , |
1043 | out_value: &value)) { |
1044 | or->hstx_trim.value = (u8)value; |
1045 | or->hstx_trim.override = true; |
1046 | } |
1047 | |
1048 | if (!of_property_read_u32(np: dev->of_node, propname: "qcom,preemphasis-level" , |
1049 | out_value: &value)) { |
1050 | or->preemphasis.value = (u8)value; |
1051 | or->preemphasis.override = true; |
1052 | } |
1053 | |
1054 | if (!of_property_read_u32(np: dev->of_node, propname: "qcom,preemphasis-width" , |
1055 | out_value: &value)) { |
1056 | or->preemphasis_width.value = (u8)value; |
1057 | or->preemphasis_width.override = true; |
1058 | } |
1059 | |
1060 | if (!of_property_read_u32(np: dev->of_node, propname: "qcom,hsdisc-trim-value" , |
1061 | out_value: &value)) { |
1062 | or->hsdisc_trim.value = (u8)value; |
1063 | or->hsdisc_trim.override = true; |
1064 | } |
1065 | |
1066 | pm_runtime_set_active(dev); |
1067 | pm_runtime_enable(dev); |
1068 | /* |
1069 | * Prevent runtime pm from being ON by default. Users can enable |
1070 | * it using power/control in sysfs. |
1071 | */ |
1072 | pm_runtime_forbid(dev); |
1073 | |
1074 | generic_phy = devm_phy_create(dev, NULL, ops: &qusb2_phy_gen_ops); |
1075 | if (IS_ERR(ptr: generic_phy)) { |
1076 | ret = PTR_ERR(ptr: generic_phy); |
1077 | dev_err(dev, "failed to create phy, %d\n" , ret); |
1078 | pm_runtime_disable(dev); |
1079 | return ret; |
1080 | } |
1081 | qphy->phy = generic_phy; |
1082 | |
1083 | dev_set_drvdata(dev, data: qphy); |
1084 | phy_set_drvdata(phy: generic_phy, data: qphy); |
1085 | |
1086 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
1087 | if (!IS_ERR(ptr: phy_provider)) |
1088 | dev_info(dev, "Registered Qcom-QUSB2 phy\n" ); |
1089 | else |
1090 | pm_runtime_disable(dev); |
1091 | |
1092 | return PTR_ERR_OR_ZERO(ptr: phy_provider); |
1093 | } |
1094 | |
1095 | static struct platform_driver qusb2_phy_driver = { |
1096 | .probe = qusb2_phy_probe, |
1097 | .driver = { |
1098 | .name = "qcom-qusb2-phy" , |
1099 | .pm = &qusb2_phy_pm_ops, |
1100 | .of_match_table = qusb2_phy_of_match_table, |
1101 | }, |
1102 | }; |
1103 | |
1104 | module_platform_driver(qusb2_phy_driver); |
1105 | |
1106 | MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>" ); |
1107 | MODULE_DESCRIPTION("Qualcomm QUSB2 PHY driver" ); |
1108 | MODULE_LICENSE("GPL v2" ); |
1109 | |