1 | // SPDX-License-Identifier: GPL-2.0 |
---|---|
2 | /* |
3 | * Copyright (c) 2023, Linaro Limited |
4 | */ |
5 | |
6 | #include <linux/bitfield.h> |
7 | #include <linux/clk.h> |
8 | #include <linux/delay.h> |
9 | #include <linux/iopoll.h> |
10 | #include <linux/mod_devicetable.h> |
11 | #include <linux/phy/phy.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/regulator/consumer.h> |
14 | #include <linux/reset.h> |
15 | |
16 | #define EXYNOS_USB_PHY_HS_PHY_CTRL_RST (0x0) |
17 | #define USB_PHY_RST_MASK GENMASK(1, 0) |
18 | #define UTMI_PORT_RST_MASK GENMASK(5, 4) |
19 | |
20 | #define EXYNOS_USB_PHY_HS_PHY_CTRL_COMMON (0x4) |
21 | #define RPTR_MODE BIT(10) |
22 | #define FSEL_20_MHZ_VAL (0x1) |
23 | #define FSEL_24_MHZ_VAL (0x2) |
24 | #define FSEL_26_MHZ_VAL (0x3) |
25 | #define FSEL_48_MHZ_VAL (0x2) |
26 | |
27 | #define EXYNOS_USB_PHY_CFG_PLLCFG0 (0x8) |
28 | #define PHY_CFG_PLL_FB_DIV_19_8_MASK GENMASK(19, 8) |
29 | #define DIV_19_8_19_2_MHZ_VAL (0x170) |
30 | #define DIV_19_8_20_MHZ_VAL (0x160) |
31 | #define DIV_19_8_24_MHZ_VAL (0x120) |
32 | #define DIV_19_8_26_MHZ_VAL (0x107) |
33 | #define DIV_19_8_48_MHZ_VAL (0x120) |
34 | |
35 | #define EXYNOS_USB_PHY_CFG_PLLCFG1 (0xc) |
36 | #define EXYNOS_PHY_CFG_PLL_FB_DIV_11_8_MASK GENMASK(11, 8) |
37 | #define EXYNOS_DIV_11_8_19_2_MHZ_VAL (0x0) |
38 | #define EXYNOS_DIV_11_8_20_MHZ_VAL (0x0) |
39 | #define EXYNOS_DIV_11_8_24_MHZ_VAL (0x0) |
40 | #define EXYNOS_DIV_11_8_26_MHZ_VAL (0x0) |
41 | #define EXYNOS_DIV_11_8_48_MHZ_VAL (0x1) |
42 | |
43 | #define EXYNOS_PHY_CFG_TX (0x14) |
44 | #define EXYNOS_PHY_CFG_TX_FSLS_VREF_TUNE_MASK GENMASK(2, 1) |
45 | |
46 | #define EXYNOS_USB_PHY_UTMI_TESTSE (0x20) |
47 | #define TEST_IDDQ BIT(6) |
48 | |
49 | #define QCOM_USB_PHY_UTMI_CTRL0 (0x3c) |
50 | #define SLEEPM BIT(0) |
51 | #define OPMODE_MASK GENMASK(4, 3) |
52 | #define OPMODE_NONDRIVING BIT(3) |
53 | |
54 | #define QCOM_USB_PHY_UTMI_CTRL5 (0x50) |
55 | #define POR BIT(1) |
56 | |
57 | #define QCOM_USB_PHY_HS_PHY_CTRL_COMMON0 (0x54) |
58 | #define PHY_ENABLE BIT(0) |
59 | #define SIDDQ_SEL BIT(1) |
60 | #define SIDDQ BIT(2) |
61 | #define RETENABLEN BIT(3) |
62 | #define FSEL_MASK GENMASK(6, 4) |
63 | #define FSEL_19_2_MHZ_VAL (0x0) |
64 | #define FSEL_38_4_MHZ_VAL (0x4) |
65 | |
66 | #define QCOM_USB_PHY_CFG_CTRL_1 (0x58) |
67 | #define PHY_CFG_PLL_CPBIAS_CNTRL_MASK GENMASK(7, 1) |
68 | |
69 | #define QCOM_USB_PHY_CFG_CTRL_2 (0x5c) |
70 | #define PHY_CFG_PLL_FB_DIV_7_0_MASK GENMASK(7, 0) |
71 | #define DIV_7_0_19_2_MHZ_VAL (0x90) |
72 | #define DIV_7_0_38_4_MHZ_VAL (0xc8) |
73 | |
74 | #define QCOM_USB_PHY_CFG_CTRL_3 (0x60) |
75 | #define PHY_CFG_PLL_FB_DIV_11_8_MASK GENMASK(3, 0) |
76 | #define DIV_11_8_19_2_MHZ_VAL (0x1) |
77 | #define DIV_11_8_38_4_MHZ_VAL (0x0) |
78 | |
79 | #define PHY_CFG_PLL_REF_DIV GENMASK(7, 4) |
80 | #define PLL_REF_DIV_VAL (0x0) |
81 | |
82 | #define QCOM_USB_PHY_HS_PHY_CTRL2 (0x64) |
83 | #define VBUSVLDEXT0 BIT(0) |
84 | #define USB2_SUSPEND_N BIT(2) |
85 | #define USB2_SUSPEND_N_SEL BIT(3) |
86 | #define VBUS_DET_EXT_SEL BIT(4) |
87 | |
88 | #define QCOM_USB_PHY_CFG_CTRL_4 (0x68) |
89 | #define PHY_CFG_PLL_GMP_CNTRL_MASK GENMASK(1, 0) |
90 | #define PHY_CFG_PLL_INT_CNTRL_MASK GENMASK(7, 2) |
91 | |
92 | #define QCOM_USB_PHY_CFG_CTRL_5 (0x6c) |
93 | #define PHY_CFG_PLL_PROP_CNTRL_MASK GENMASK(4, 0) |
94 | #define PHY_CFG_PLL_VREF_TUNE_MASK GENMASK(7, 6) |
95 | |
96 | #define QCOM_USB_PHY_CFG_CTRL_6 (0x70) |
97 | #define PHY_CFG_PLL_VCO_CNTRL_MASK GENMASK(2, 0) |
98 | |
99 | #define QCOM_USB_PHY_CFG_CTRL_7 (0x74) |
100 | |
101 | #define QCOM_USB_PHY_CFG_CTRL_8 (0x78) |
102 | #define PHY_CFG_TX_FSLS_VREF_TUNE_MASK GENMASK(1, 0) |
103 | #define PHY_CFG_TX_FSLS_VREG_BYPASS BIT(2) |
104 | #define PHY_CFG_TX_HS_VREF_TUNE_MASK GENMASK(5, 3) |
105 | #define PHY_CFG_TX_HS_XV_TUNE_MASK GENMASK(7, 6) |
106 | |
107 | #define QCOM_USB_PHY_CFG_CTRL_9 (0x7c) |
108 | #define PHY_CFG_TX_PREEMP_TUNE_MASK GENMASK(2, 0) |
109 | #define PHY_CFG_TX_RES_TUNE_MASK GENMASK(4, 3) |
110 | #define PHY_CFG_TX_RISE_TUNE_MASK GENMASK(6, 5) |
111 | #define PHY_CFG_RCAL_BYPASS BIT(7) |
112 | |
113 | #define QCOM_USB_PHY_CFG_CTRL_10 (0x80) |
114 | |
115 | #define QCOM_USB_PHY_CFG0 (0x94) |
116 | #define DATAPATH_CTRL_OVERRIDE_EN BIT(0) |
117 | #define CMN_CTRL_OVERRIDE_EN BIT(1) |
118 | |
119 | #define QCOM_UTMI_PHY_CMN_CTRL0 (0x98) |
120 | #define TESTBURNIN BIT(6) |
121 | |
122 | #define QCOM_USB_PHY_FSEL_SEL (0xb8) |
123 | #define FSEL_SEL BIT(0) |
124 | |
125 | #define QCOM_USB_PHY_APB_ACCESS_CMD (0x130) |
126 | #define RW_ACCESS BIT(0) |
127 | #define APB_START_CMD BIT(1) |
128 | #define APB_LOGIC_RESET BIT(2) |
129 | |
130 | #define QCOM_USB_PHY_APB_ACCESS_STATUS (0x134) |
131 | #define ACCESS_DONE BIT(0) |
132 | #define TIMED_OUT BIT(1) |
133 | #define ACCESS_ERROR BIT(2) |
134 | #define ACCESS_IN_PROGRESS BIT(3) |
135 | |
136 | #define QCOM_USB_PHY_APB_ADDRESS (0x138) |
137 | #define APB_REG_ADDR_MASK GENMASK(7, 0) |
138 | |
139 | #define QCOM_USB_PHY_APB_WRDATA_LSB (0x13c) |
140 | #define APB_REG_WRDATA_7_0_MASK GENMASK(3, 0) |
141 | |
142 | #define QCOM_USB_PHY_APB_WRDATA_MSB (0x140) |
143 | #define APB_REG_WRDATA_15_8_MASK GENMASK(7, 4) |
144 | |
145 | #define QCOM_USB_PHY_APB_RDDATA_LSB (0x144) |
146 | #define APB_REG_RDDATA_7_0_MASK GENMASK(3, 0) |
147 | |
148 | #define QCOM_USB_PHY_APB_RDDATA_MSB (0x148) |
149 | #define APB_REG_RDDATA_15_8_MASK GENMASK(7, 4) |
150 | |
151 | static const char * const eusb2_hsphy_vreg_names[] = { |
152 | "vdd", "vdda12", |
153 | }; |
154 | |
155 | #define EUSB2_NUM_VREGS ARRAY_SIZE(eusb2_hsphy_vreg_names) |
156 | |
157 | struct snps_eusb2_phy_drvdata { |
158 | int (*phy_init)(struct phy *p); |
159 | const char * const *clk_names; |
160 | int num_clks; |
161 | }; |
162 | |
163 | struct snps_eusb2_hsphy { |
164 | struct phy *phy; |
165 | void __iomem *base; |
166 | |
167 | struct clk *ref_clk; |
168 | struct clk_bulk_data *clks; |
169 | struct reset_control *phy_reset; |
170 | |
171 | struct regulator_bulk_data vregs[EUSB2_NUM_VREGS]; |
172 | |
173 | enum phy_mode mode; |
174 | |
175 | struct phy *repeater; |
176 | |
177 | const struct snps_eusb2_phy_drvdata *data; |
178 | }; |
179 | |
180 | static int snps_eusb2_hsphy_set_mode(struct phy *p, enum phy_mode mode, int submode) |
181 | { |
182 | struct snps_eusb2_hsphy *phy = phy_get_drvdata(phy: p); |
183 | |
184 | phy->mode = mode; |
185 | |
186 | return phy_set_mode_ext(phy: phy->repeater, mode, submode); |
187 | } |
188 | |
189 | static void snps_eusb2_hsphy_write_mask(void __iomem *base, u32 offset, |
190 | u32 mask, u32 val) |
191 | { |
192 | u32 reg; |
193 | |
194 | reg = readl_relaxed(base + offset); |
195 | reg &= ~mask; |
196 | reg |= val & mask; |
197 | writel_relaxed(reg, base + offset); |
198 | |
199 | /* Ensure above write is completed */ |
200 | readl_relaxed(base + offset); |
201 | } |
202 | |
203 | static void qcom_eusb2_default_parameters(struct snps_eusb2_hsphy *phy) |
204 | { |
205 | /* default parameters: tx pre-emphasis */ |
206 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_CFG_CTRL_9, |
207 | PHY_CFG_TX_PREEMP_TUNE_MASK, |
208 | FIELD_PREP(PHY_CFG_TX_PREEMP_TUNE_MASK, 0)); |
209 | |
210 | /* tx rise/fall time */ |
211 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_CFG_CTRL_9, |
212 | PHY_CFG_TX_RISE_TUNE_MASK, |
213 | FIELD_PREP(PHY_CFG_TX_RISE_TUNE_MASK, 0x2)); |
214 | |
215 | /* source impedance adjustment */ |
216 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_CFG_CTRL_9, |
217 | PHY_CFG_TX_RES_TUNE_MASK, |
218 | FIELD_PREP(PHY_CFG_TX_RES_TUNE_MASK, 0x1)); |
219 | |
220 | /* dc voltage level adjustement */ |
221 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_CFG_CTRL_8, |
222 | PHY_CFG_TX_HS_VREF_TUNE_MASK, |
223 | FIELD_PREP(PHY_CFG_TX_HS_VREF_TUNE_MASK, 0x3)); |
224 | |
225 | /* transmitter HS crossover adjustement */ |
226 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_CFG_CTRL_8, |
227 | PHY_CFG_TX_HS_XV_TUNE_MASK, |
228 | FIELD_PREP(PHY_CFG_TX_HS_XV_TUNE_MASK, 0x0)); |
229 | } |
230 | |
231 | struct snps_eusb2_ref_clk { |
232 | unsigned long freq; |
233 | u32 fsel_val; |
234 | u32 div_7_0_val; |
235 | u32 div_11_8_val; |
236 | }; |
237 | |
238 | static const struct snps_eusb2_ref_clk exynos_eusb2_ref_clk[] = { |
239 | { 19200000, FSEL_19_2_MHZ_VAL, DIV_19_8_19_2_MHZ_VAL, EXYNOS_DIV_11_8_19_2_MHZ_VAL }, |
240 | { 20000000, FSEL_20_MHZ_VAL, DIV_19_8_20_MHZ_VAL, EXYNOS_DIV_11_8_20_MHZ_VAL }, |
241 | { 24000000, FSEL_24_MHZ_VAL, DIV_19_8_24_MHZ_VAL, EXYNOS_DIV_11_8_24_MHZ_VAL }, |
242 | { 26000000, FSEL_26_MHZ_VAL, DIV_19_8_26_MHZ_VAL, EXYNOS_DIV_11_8_26_MHZ_VAL }, |
243 | { 48000000, FSEL_48_MHZ_VAL, DIV_19_8_48_MHZ_VAL, EXYNOS_DIV_11_8_48_MHZ_VAL }, |
244 | }; |
245 | |
246 | static int exynos_eusb2_ref_clk_init(struct snps_eusb2_hsphy *phy) |
247 | { |
248 | const struct snps_eusb2_ref_clk *config = NULL; |
249 | unsigned long ref_clk_freq = clk_get_rate(clk: phy->ref_clk); |
250 | |
251 | for (int i = 0; i < ARRAY_SIZE(exynos_eusb2_ref_clk); i++) { |
252 | if (exynos_eusb2_ref_clk[i].freq == ref_clk_freq) { |
253 | config = &exynos_eusb2_ref_clk[i]; |
254 | break; |
255 | } |
256 | } |
257 | |
258 | if (!config) { |
259 | dev_err(&phy->phy->dev, "unsupported ref_clk_freq:%lu\n", ref_clk_freq); |
260 | return -EINVAL; |
261 | } |
262 | |
263 | snps_eusb2_hsphy_write_mask(base: phy->base, EXYNOS_USB_PHY_HS_PHY_CTRL_COMMON, |
264 | FSEL_MASK, |
265 | FIELD_PREP(FSEL_MASK, config->fsel_val)); |
266 | |
267 | snps_eusb2_hsphy_write_mask(base: phy->base, EXYNOS_USB_PHY_CFG_PLLCFG0, |
268 | PHY_CFG_PLL_FB_DIV_19_8_MASK, |
269 | FIELD_PREP(PHY_CFG_PLL_FB_DIV_19_8_MASK, |
270 | config->div_7_0_val)); |
271 | |
272 | snps_eusb2_hsphy_write_mask(base: phy->base, EXYNOS_USB_PHY_CFG_PLLCFG1, |
273 | EXYNOS_PHY_CFG_PLL_FB_DIV_11_8_MASK, |
274 | val: config->div_11_8_val); |
275 | return 0; |
276 | } |
277 | |
278 | static const struct snps_eusb2_ref_clk qcom_eusb2_ref_clk[] = { |
279 | { 19200000, FSEL_19_2_MHZ_VAL, DIV_7_0_19_2_MHZ_VAL, DIV_11_8_19_2_MHZ_VAL }, |
280 | { 38400000, FSEL_38_4_MHZ_VAL, DIV_7_0_38_4_MHZ_VAL, DIV_11_8_38_4_MHZ_VAL }, |
281 | }; |
282 | |
283 | static int qcom_eusb2_ref_clk_init(struct snps_eusb2_hsphy *phy) |
284 | { |
285 | const struct snps_eusb2_ref_clk *config = NULL; |
286 | unsigned long ref_clk_freq = clk_get_rate(clk: phy->ref_clk); |
287 | |
288 | for (int i = 0; i < ARRAY_SIZE(qcom_eusb2_ref_clk); i++) { |
289 | if (qcom_eusb2_ref_clk[i].freq == ref_clk_freq) { |
290 | config = &qcom_eusb2_ref_clk[i]; |
291 | break; |
292 | } |
293 | } |
294 | |
295 | if (!config) { |
296 | dev_err(&phy->phy->dev, "unsupported ref_clk_freq:%lu\n", ref_clk_freq); |
297 | return -EINVAL; |
298 | } |
299 | |
300 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_HS_PHY_CTRL_COMMON0, |
301 | FSEL_MASK, |
302 | FIELD_PREP(FSEL_MASK, config->fsel_val)); |
303 | |
304 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_CFG_CTRL_2, |
305 | PHY_CFG_PLL_FB_DIV_7_0_MASK, |
306 | val: config->div_7_0_val); |
307 | |
308 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_CFG_CTRL_3, |
309 | PHY_CFG_PLL_FB_DIV_11_8_MASK, |
310 | val: config->div_11_8_val); |
311 | |
312 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_CFG_CTRL_3, |
313 | PHY_CFG_PLL_REF_DIV, PLL_REF_DIV_VAL); |
314 | |
315 | return 0; |
316 | } |
317 | |
318 | static int exynos_snps_eusb2_hsphy_init(struct phy *p) |
319 | { |
320 | struct snps_eusb2_hsphy *phy = phy_get_drvdata(phy: p); |
321 | int ret; |
322 | |
323 | snps_eusb2_hsphy_write_mask(base: phy->base, EXYNOS_USB_PHY_HS_PHY_CTRL_RST, |
324 | USB_PHY_RST_MASK | UTMI_PORT_RST_MASK, |
325 | USB_PHY_RST_MASK | UTMI_PORT_RST_MASK); |
326 | fsleep(usecs: 50); /* required after holding phy in reset */ |
327 | |
328 | snps_eusb2_hsphy_write_mask(base: phy->base, EXYNOS_USB_PHY_HS_PHY_CTRL_COMMON, |
329 | RPTR_MODE, RPTR_MODE); |
330 | |
331 | /* update ref_clk related registers */ |
332 | ret = exynos_eusb2_ref_clk_init(phy); |
333 | if (ret) |
334 | return ret; |
335 | |
336 | /* default parameter: tx fsls-vref */ |
337 | snps_eusb2_hsphy_write_mask(base: phy->base, EXYNOS_PHY_CFG_TX, |
338 | EXYNOS_PHY_CFG_TX_FSLS_VREF_TUNE_MASK, |
339 | FIELD_PREP(EXYNOS_PHY_CFG_TX_FSLS_VREF_TUNE_MASK, 0x0)); |
340 | |
341 | snps_eusb2_hsphy_write_mask(base: phy->base, EXYNOS_USB_PHY_UTMI_TESTSE, |
342 | TEST_IDDQ, val: 0); |
343 | fsleep(usecs: 10); /* required after releasing test_iddq */ |
344 | |
345 | snps_eusb2_hsphy_write_mask(base: phy->base, EXYNOS_USB_PHY_HS_PHY_CTRL_RST, |
346 | USB_PHY_RST_MASK, val: 0); |
347 | |
348 | snps_eusb2_hsphy_write_mask(base: phy->base, EXYNOS_USB_PHY_HS_PHY_CTRL_COMMON, |
349 | PHY_ENABLE, PHY_ENABLE); |
350 | |
351 | snps_eusb2_hsphy_write_mask(base: phy->base, EXYNOS_USB_PHY_HS_PHY_CTRL_RST, |
352 | UTMI_PORT_RST_MASK, val: 0); |
353 | |
354 | return 0; |
355 | } |
356 | |
357 | static const char * const exynos_eusb2_hsphy_clock_names[] = { |
358 | "ref", "bus", "ctrl", |
359 | }; |
360 | |
361 | static const struct snps_eusb2_phy_drvdata exynos2200_snps_eusb2_phy = { |
362 | .phy_init = exynos_snps_eusb2_hsphy_init, |
363 | .clk_names = exynos_eusb2_hsphy_clock_names, |
364 | .num_clks = ARRAY_SIZE(exynos_eusb2_hsphy_clock_names), |
365 | }; |
366 | |
367 | static int qcom_snps_eusb2_hsphy_init(struct phy *p) |
368 | { |
369 | struct snps_eusb2_hsphy *phy = phy_get_drvdata(phy: p); |
370 | int ret; |
371 | |
372 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_CFG0, |
373 | CMN_CTRL_OVERRIDE_EN, CMN_CTRL_OVERRIDE_EN); |
374 | |
375 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_UTMI_CTRL5, POR, POR); |
376 | |
377 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_HS_PHY_CTRL_COMMON0, |
378 | PHY_ENABLE | RETENABLEN, PHY_ENABLE | RETENABLEN); |
379 | |
380 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_APB_ACCESS_CMD, |
381 | APB_LOGIC_RESET, APB_LOGIC_RESET); |
382 | |
383 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_UTMI_PHY_CMN_CTRL0, TESTBURNIN, val: 0); |
384 | |
385 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_FSEL_SEL, |
386 | FSEL_SEL, FSEL_SEL); |
387 | |
388 | /* update ref_clk related registers */ |
389 | ret = qcom_eusb2_ref_clk_init(phy); |
390 | if (ret) |
391 | return ret; |
392 | |
393 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_CFG_CTRL_1, |
394 | PHY_CFG_PLL_CPBIAS_CNTRL_MASK, |
395 | FIELD_PREP(PHY_CFG_PLL_CPBIAS_CNTRL_MASK, 0x1)); |
396 | |
397 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_CFG_CTRL_4, |
398 | PHY_CFG_PLL_INT_CNTRL_MASK, |
399 | FIELD_PREP(PHY_CFG_PLL_INT_CNTRL_MASK, 0x8)); |
400 | |
401 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_CFG_CTRL_4, |
402 | PHY_CFG_PLL_GMP_CNTRL_MASK, |
403 | FIELD_PREP(PHY_CFG_PLL_GMP_CNTRL_MASK, 0x1)); |
404 | |
405 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_CFG_CTRL_5, |
406 | PHY_CFG_PLL_PROP_CNTRL_MASK, |
407 | FIELD_PREP(PHY_CFG_PLL_PROP_CNTRL_MASK, 0x10)); |
408 | |
409 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_CFG_CTRL_6, |
410 | PHY_CFG_PLL_VCO_CNTRL_MASK, |
411 | FIELD_PREP(PHY_CFG_PLL_VCO_CNTRL_MASK, 0x0)); |
412 | |
413 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_CFG_CTRL_5, |
414 | PHY_CFG_PLL_VREF_TUNE_MASK, |
415 | FIELD_PREP(PHY_CFG_PLL_VREF_TUNE_MASK, 0x1)); |
416 | |
417 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_HS_PHY_CTRL2, |
418 | VBUS_DET_EXT_SEL, VBUS_DET_EXT_SEL); |
419 | |
420 | /* set default parameters */ |
421 | qcom_eusb2_default_parameters(phy); |
422 | |
423 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_HS_PHY_CTRL2, |
424 | USB2_SUSPEND_N_SEL | USB2_SUSPEND_N, |
425 | USB2_SUSPEND_N_SEL | USB2_SUSPEND_N); |
426 | |
427 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_UTMI_CTRL0, SLEEPM, SLEEPM); |
428 | |
429 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_HS_PHY_CTRL_COMMON0, |
430 | SIDDQ_SEL, SIDDQ_SEL); |
431 | |
432 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_HS_PHY_CTRL_COMMON0, |
433 | SIDDQ, val: 0); |
434 | |
435 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_UTMI_CTRL5, POR, val: 0); |
436 | |
437 | snps_eusb2_hsphy_write_mask(base: phy->base, QCOM_USB_PHY_HS_PHY_CTRL2, |
438 | USB2_SUSPEND_N_SEL, val: 0); |
439 | |
440 | return 0; |
441 | } |
442 | |
443 | static const char * const qcom_eusb2_hsphy_clock_names[] = { |
444 | "ref", |
445 | }; |
446 | |
447 | static const struct snps_eusb2_phy_drvdata sm8550_snps_eusb2_phy = { |
448 | .phy_init = qcom_snps_eusb2_hsphy_init, |
449 | .clk_names = qcom_eusb2_hsphy_clock_names, |
450 | .num_clks = ARRAY_SIZE(qcom_eusb2_hsphy_clock_names), |
451 | }; |
452 | |
453 | static int snps_eusb2_hsphy_init(struct phy *p) |
454 | { |
455 | struct snps_eusb2_hsphy *phy = phy_get_drvdata(phy: p); |
456 | int ret; |
457 | |
458 | ret = regulator_bulk_enable(ARRAY_SIZE(phy->vregs), consumers: phy->vregs); |
459 | if (ret) |
460 | return ret; |
461 | |
462 | ret = phy_init(phy: phy->repeater); |
463 | if (ret) { |
464 | dev_err(&p->dev, "repeater init failed. %d\n", ret); |
465 | goto disable_vreg; |
466 | } |
467 | |
468 | ret = clk_bulk_prepare_enable(num_clks: phy->data->num_clks, clks: phy->clks); |
469 | if (ret) { |
470 | dev_err(&p->dev, "failed to enable ref clock, %d\n", ret); |
471 | goto disable_vreg; |
472 | } |
473 | |
474 | ret = reset_control_assert(rstc: phy->phy_reset); |
475 | if (ret) { |
476 | dev_err(&p->dev, "failed to assert phy_reset, %d\n", ret); |
477 | goto disable_ref_clk; |
478 | } |
479 | |
480 | usleep_range(min: 100, max: 150); |
481 | |
482 | ret = reset_control_deassert(rstc: phy->phy_reset); |
483 | if (ret) { |
484 | dev_err(&p->dev, "failed to de-assert phy_reset, %d\n", ret); |
485 | goto disable_ref_clk; |
486 | } |
487 | |
488 | ret = phy->data->phy_init(p); |
489 | if (ret) |
490 | goto disable_ref_clk; |
491 | |
492 | return 0; |
493 | |
494 | disable_ref_clk: |
495 | clk_bulk_disable_unprepare(num_clks: phy->data->num_clks, clks: phy->clks); |
496 | |
497 | disable_vreg: |
498 | regulator_bulk_disable(ARRAY_SIZE(phy->vregs), consumers: phy->vregs); |
499 | |
500 | return ret; |
501 | } |
502 | |
503 | static int snps_eusb2_hsphy_exit(struct phy *p) |
504 | { |
505 | struct snps_eusb2_hsphy *phy = phy_get_drvdata(phy: p); |
506 | |
507 | clk_disable_unprepare(clk: phy->ref_clk); |
508 | |
509 | regulator_bulk_disable(ARRAY_SIZE(phy->vregs), consumers: phy->vregs); |
510 | |
511 | phy_exit(phy: phy->repeater); |
512 | |
513 | return 0; |
514 | } |
515 | |
516 | static const struct phy_ops snps_eusb2_hsphy_ops = { |
517 | .init = snps_eusb2_hsphy_init, |
518 | .exit = snps_eusb2_hsphy_exit, |
519 | .set_mode = snps_eusb2_hsphy_set_mode, |
520 | .owner = THIS_MODULE, |
521 | }; |
522 | |
523 | static int snps_eusb2_hsphy_probe(struct platform_device *pdev) |
524 | { |
525 | struct device *dev = &pdev->dev; |
526 | struct device_node *np = dev->of_node; |
527 | struct snps_eusb2_hsphy *phy; |
528 | struct phy_provider *phy_provider; |
529 | struct phy *generic_phy; |
530 | int ret, i; |
531 | int num; |
532 | |
533 | phy = devm_kzalloc(dev, size: sizeof(*phy), GFP_KERNEL); |
534 | if (!phy) |
535 | return -ENOMEM; |
536 | |
537 | phy->data = device_get_match_data(dev); |
538 | if (!phy->data) |
539 | return -EINVAL; |
540 | |
541 | phy->base = devm_platform_ioremap_resource(pdev, index: 0); |
542 | if (IS_ERR(ptr: phy->base)) |
543 | return PTR_ERR(ptr: phy->base); |
544 | |
545 | phy->phy_reset = devm_reset_control_get_optional_exclusive(dev, NULL); |
546 | if (IS_ERR(ptr: phy->phy_reset)) |
547 | return PTR_ERR(ptr: phy->phy_reset); |
548 | |
549 | phy->clks = devm_kcalloc(dev, n: phy->data->num_clks, size: sizeof(*phy->clks), |
550 | GFP_KERNEL); |
551 | if (!phy->clks) |
552 | return -ENOMEM; |
553 | |
554 | for (int i = 0; i < phy->data->num_clks; ++i) |
555 | phy->clks[i].id = phy->data->clk_names[i]; |
556 | |
557 | ret = devm_clk_bulk_get(dev, num_clks: phy->data->num_clks, clks: phy->clks); |
558 | if (ret) |
559 | return dev_err_probe(dev, err: ret, |
560 | fmt: "failed to get phy clock(s)\n"); |
561 | |
562 | phy->ref_clk = NULL; |
563 | for (int i = 0; i < phy->data->num_clks; ++i) { |
564 | if (!strcmp(phy->clks[i].id, "ref")) { |
565 | phy->ref_clk = phy->clks[i].clk; |
566 | break; |
567 | } |
568 | } |
569 | |
570 | if (IS_ERR_OR_NULL(ptr: phy->ref_clk)) |
571 | return dev_err_probe(dev, err: PTR_ERR(ptr: phy->ref_clk), |
572 | fmt: "failed to get ref clk\n"); |
573 | |
574 | num = ARRAY_SIZE(phy->vregs); |
575 | for (i = 0; i < num; i++) |
576 | phy->vregs[i].supply = eusb2_hsphy_vreg_names[i]; |
577 | |
578 | ret = devm_regulator_bulk_get(dev, num_consumers: num, consumers: phy->vregs); |
579 | if (ret) |
580 | return dev_err_probe(dev, err: ret, |
581 | fmt: "failed to get regulator supplies\n"); |
582 | |
583 | phy->repeater = devm_of_phy_optional_get(dev, np, con_id: 0); |
584 | if (IS_ERR(ptr: phy->repeater)) |
585 | return dev_err_probe(dev, err: PTR_ERR(ptr: phy->repeater), |
586 | fmt: "failed to get repeater\n"); |
587 | |
588 | generic_phy = devm_phy_create(dev, NULL, ops: &snps_eusb2_hsphy_ops); |
589 | if (IS_ERR(ptr: generic_phy)) { |
590 | dev_err(dev, "failed to create phy %d\n", ret); |
591 | return PTR_ERR(ptr: generic_phy); |
592 | } |
593 | |
594 | dev_set_drvdata(dev, data: phy); |
595 | phy_set_drvdata(phy: generic_phy, data: phy); |
596 | |
597 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
598 | if (IS_ERR(ptr: phy_provider)) |
599 | return PTR_ERR(ptr: phy_provider); |
600 | |
601 | dev_info(dev, "Registered Snps-eUSB2 phy\n"); |
602 | |
603 | return 0; |
604 | } |
605 | |
606 | static const struct of_device_id snps_eusb2_hsphy_of_match_table[] = { |
607 | { |
608 | .compatible = "qcom,sm8550-snps-eusb2-phy", |
609 | .data = &sm8550_snps_eusb2_phy, |
610 | }, { |
611 | .compatible = "samsung,exynos2200-eusb2-phy", |
612 | .data = &exynos2200_snps_eusb2_phy, |
613 | }, { }, |
614 | }; |
615 | MODULE_DEVICE_TABLE(of, snps_eusb2_hsphy_of_match_table); |
616 | |
617 | static struct platform_driver snps_eusb2_hsphy_driver = { |
618 | .probe = snps_eusb2_hsphy_probe, |
619 | .driver = { |
620 | .name = "snps-eusb2-hsphy", |
621 | .of_match_table = snps_eusb2_hsphy_of_match_table, |
622 | }, |
623 | }; |
624 | |
625 | module_platform_driver(snps_eusb2_hsphy_driver); |
626 | MODULE_DESCRIPTION("Synopsys eUSB2 HS PHY driver"); |
627 | MODULE_LICENSE("GPL"); |
628 |
Definitions
- eusb2_hsphy_vreg_names
- snps_eusb2_phy_drvdata
- snps_eusb2_hsphy
- snps_eusb2_hsphy_set_mode
- snps_eusb2_hsphy_write_mask
- qcom_eusb2_default_parameters
- snps_eusb2_ref_clk
- exynos_eusb2_ref_clk
- exynos_eusb2_ref_clk_init
- qcom_eusb2_ref_clk
- qcom_eusb2_ref_clk_init
- exynos_snps_eusb2_hsphy_init
- exynos_eusb2_hsphy_clock_names
- exynos2200_snps_eusb2_phy
- qcom_snps_eusb2_hsphy_init
- qcom_eusb2_hsphy_clock_names
- sm8550_snps_eusb2_phy
- snps_eusb2_hsphy_init
- snps_eusb2_hsphy_exit
- snps_eusb2_hsphy_ops
- snps_eusb2_hsphy_probe
- snps_eusb2_hsphy_of_match_table
Improve your Profiling and Debugging skills
Find out more