1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Samsung Exynos5 SoC series USB DRD PHY driver |
4 | * |
5 | * Phy provider for USB 3.0 DRD controller on Exynos5 SoC series |
6 | * |
7 | * Copyright (C) 2014 Samsung Electronics Co., Ltd. |
8 | * Author: Vivek Gautam <gautam.vivek@samsung.com> |
9 | */ |
10 | |
11 | #include <linux/clk.h> |
12 | #include <linux/delay.h> |
13 | #include <linux/io.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> |
16 | #include <linux/of.h> |
17 | #include <linux/iopoll.h> |
18 | #include <linux/phy/phy.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/mutex.h> |
21 | #include <linux/mfd/syscon.h> |
22 | #include <linux/regmap.h> |
23 | #include <linux/regulator/consumer.h> |
24 | #include <linux/soc/samsung/exynos-regs-pmu.h> |
25 | |
26 | /* Exynos USB PHY registers */ |
27 | #define EXYNOS5_FSEL_9MHZ6 0x0 |
28 | #define EXYNOS5_FSEL_10MHZ 0x1 |
29 | #define EXYNOS5_FSEL_12MHZ 0x2 |
30 | #define EXYNOS5_FSEL_19MHZ2 0x3 |
31 | #define EXYNOS5_FSEL_20MHZ 0x4 |
32 | #define EXYNOS5_FSEL_24MHZ 0x5 |
33 | #define EXYNOS5_FSEL_26MHZ 0x82 |
34 | #define EXYNOS5_FSEL_50MHZ 0x7 |
35 | |
36 | /* Exynos5: USB 3.0 DRD PHY registers */ |
37 | #define EXYNOS5_DRD_LINKSYSTEM 0x04 |
38 | |
39 | #define LINKSYSTEM_FLADJ_MASK (0x3f << 1) |
40 | #define LINKSYSTEM_FLADJ(_x) ((_x) << 1) |
41 | #define LINKSYSTEM_XHCI_VERSION_CONTROL BIT(27) |
42 | |
43 | #define EXYNOS5_DRD_PHYUTMI 0x08 |
44 | |
45 | #define PHYUTMI_OTGDISABLE BIT(6) |
46 | #define PHYUTMI_FORCESUSPEND BIT(1) |
47 | #define PHYUTMI_FORCESLEEP BIT(0) |
48 | |
49 | #define EXYNOS5_DRD_PHYPIPE 0x0c |
50 | |
51 | #define EXYNOS5_DRD_PHYCLKRST 0x10 |
52 | |
53 | #define PHYCLKRST_EN_UTMISUSPEND BIT(31) |
54 | |
55 | #define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23) |
56 | #define PHYCLKRST_SSC_REFCLKSEL(_x) ((_x) << 23) |
57 | |
58 | #define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21) |
59 | #define PHYCLKRST_SSC_RANGE(_x) ((_x) << 21) |
60 | |
61 | #define PHYCLKRST_SSC_EN BIT(20) |
62 | #define PHYCLKRST_REF_SSP_EN BIT(19) |
63 | #define PHYCLKRST_REF_CLKDIV2 BIT(18) |
64 | |
65 | #define PHYCLKRST_MPLL_MULTIPLIER_MASK (0x7f << 11) |
66 | #define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF (0x19 << 11) |
67 | #define PHYCLKRST_MPLL_MULTIPLIER_50M_REF (0x32 << 11) |
68 | #define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF (0x68 << 11) |
69 | #define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF (0x7d << 11) |
70 | #define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF (0x02 << 11) |
71 | |
72 | #define PHYCLKRST_FSEL_UTMI_MASK (0x7 << 5) |
73 | #define PHYCLKRST_FSEL_PIPE_MASK (0x7 << 8) |
74 | #define PHYCLKRST_FSEL(_x) ((_x) << 5) |
75 | #define PHYCLKRST_FSEL_PAD_100MHZ (0x27 << 5) |
76 | #define PHYCLKRST_FSEL_PAD_24MHZ (0x2a << 5) |
77 | #define PHYCLKRST_FSEL_PAD_20MHZ (0x31 << 5) |
78 | #define PHYCLKRST_FSEL_PAD_19_2MHZ (0x38 << 5) |
79 | |
80 | #define PHYCLKRST_RETENABLEN BIT(4) |
81 | |
82 | #define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2) |
83 | #define PHYCLKRST_REFCLKSEL_PAD_REFCLK (0x2 << 2) |
84 | #define PHYCLKRST_REFCLKSEL_EXT_REFCLK (0x3 << 2) |
85 | |
86 | #define PHYCLKRST_PORTRESET BIT(1) |
87 | #define PHYCLKRST_COMMONONN BIT(0) |
88 | |
89 | #define EXYNOS5_DRD_PHYREG0 0x14 |
90 | #define PHYREG0_SSC_REF_CLK_SEL BIT(21) |
91 | #define PHYREG0_SSC_RANGE BIT(20) |
92 | #define PHYREG0_CR_WRITE BIT(19) |
93 | #define PHYREG0_CR_READ BIT(18) |
94 | #define PHYREG0_CR_DATA_IN(_x) ((_x) << 2) |
95 | #define PHYREG0_CR_CAP_DATA BIT(1) |
96 | #define PHYREG0_CR_CAP_ADDR BIT(0) |
97 | |
98 | #define EXYNOS5_DRD_PHYREG1 0x18 |
99 | #define PHYREG1_CR_DATA_OUT(_x) ((_x) << 1) |
100 | #define PHYREG1_CR_ACK BIT(0) |
101 | |
102 | #define EXYNOS5_DRD_PHYPARAM0 0x1c |
103 | |
104 | #define PHYPARAM0_REF_USE_PAD BIT(31) |
105 | #define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26) |
106 | #define PHYPARAM0_REF_LOSLEVEL (0x9 << 26) |
107 | |
108 | #define EXYNOS5_DRD_PHYPARAM1 0x20 |
109 | |
110 | #define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0) |
111 | #define PHYPARAM1_PCS_TXDEEMPH (0x1c) |
112 | |
113 | #define EXYNOS5_DRD_PHYTERM 0x24 |
114 | |
115 | #define EXYNOS5_DRD_PHYTEST 0x28 |
116 | |
117 | #define PHYTEST_POWERDOWN_SSP BIT(3) |
118 | #define PHYTEST_POWERDOWN_HSP BIT(2) |
119 | |
120 | #define EXYNOS5_DRD_PHYADP 0x2c |
121 | |
122 | #define EXYNOS5_DRD_PHYUTMICLKSEL 0x30 |
123 | |
124 | #define PHYUTMICLKSEL_UTMI_CLKSEL BIT(2) |
125 | |
126 | #define EXYNOS5_DRD_PHYRESUME 0x34 |
127 | #define EXYNOS5_DRD_LINKPORT 0x44 |
128 | |
129 | /* USB 3.0 DRD PHY SS Function Control Reg; accessed by CR_PORT */ |
130 | #define EXYNOS5_DRD_PHYSS_LOSLEVEL_OVRD_IN (0x15) |
131 | #define LOSLEVEL_OVRD_IN_LOS_BIAS_5420 (0x5 << 13) |
132 | #define LOSLEVEL_OVRD_IN_LOS_BIAS_DEFAULT (0x0 << 13) |
133 | #define LOSLEVEL_OVRD_IN_EN (0x1 << 10) |
134 | #define LOSLEVEL_OVRD_IN_LOS_LEVEL_DEFAULT (0x9 << 0) |
135 | |
136 | #define EXYNOS5_DRD_PHYSS_TX_VBOOSTLEVEL_OVRD_IN (0x12) |
137 | #define TX_VBOOSTLEVEL_OVRD_IN_VBOOST_5420 (0x5 << 13) |
138 | #define TX_VBOOSTLEVEL_OVRD_IN_VBOOST_DEFAULT (0x4 << 13) |
139 | |
140 | #define EXYNOS5_DRD_PHYSS_LANE0_TX_DEBUG (0x1010) |
141 | #define LANE0_TX_DEBUG_RXDET_MEAS_TIME_19M2_20M (0x4 << 4) |
142 | #define LANE0_TX_DEBUG_RXDET_MEAS_TIME_24M (0x8 << 4) |
143 | #define LANE0_TX_DEBUG_RXDET_MEAS_TIME_25M_26M (0x8 << 4) |
144 | #define LANE0_TX_DEBUG_RXDET_MEAS_TIME_48M_50M_52M (0x20 << 4) |
145 | #define LANE0_TX_DEBUG_RXDET_MEAS_TIME_62M5 (0x20 << 4) |
146 | #define LANE0_TX_DEBUG_RXDET_MEAS_TIME_96M_100M (0x40 << 4) |
147 | |
148 | /* Exynos850: USB DRD PHY registers */ |
149 | #define EXYNOS850_DRD_LINKCTRL 0x04 |
150 | #define LINKCTRL_BUS_FILTER_BYPASS(_x) ((_x) << 4) |
151 | #define LINKCTRL_FORCE_QACT BIT(8) |
152 | |
153 | #define EXYNOS850_DRD_CLKRST 0x20 |
154 | #define CLKRST_LINK_SW_RST BIT(0) |
155 | #define CLKRST_PORT_RST BIT(1) |
156 | #define CLKRST_PHY_SW_RST BIT(3) |
157 | |
158 | #define EXYNOS850_DRD_UTMI 0x50 |
159 | #define UTMI_FORCE_SLEEP BIT(0) |
160 | #define UTMI_FORCE_SUSPEND BIT(1) |
161 | #define UTMI_DM_PULLDOWN BIT(2) |
162 | #define UTMI_DP_PULLDOWN BIT(3) |
163 | #define UTMI_FORCE_BVALID BIT(4) |
164 | #define UTMI_FORCE_VBUSVALID BIT(5) |
165 | |
166 | #define EXYNOS850_DRD_HSP 0x54 |
167 | #define HSP_COMMONONN BIT(8) |
168 | #define HSP_EN_UTMISUSPEND BIT(9) |
169 | #define HSP_VBUSVLDEXT BIT(12) |
170 | #define HSP_VBUSVLDEXTSEL BIT(13) |
171 | #define HSP_FSV_OUT_EN BIT(24) |
172 | |
173 | #define EXYNOS850_DRD_HSP_TEST 0x5c |
174 | #define HSP_TEST_SIDDQ BIT(24) |
175 | |
176 | #define KHZ 1000 |
177 | #define MHZ (KHZ * KHZ) |
178 | |
179 | enum exynos5_usbdrd_phy_id { |
180 | EXYNOS5_DRDPHY_UTMI, |
181 | EXYNOS5_DRDPHY_PIPE3, |
182 | EXYNOS5_DRDPHYS_NUM, |
183 | }; |
184 | |
185 | struct phy_usb_instance; |
186 | struct exynos5_usbdrd_phy; |
187 | |
188 | struct exynos5_usbdrd_phy_config { |
189 | u32 id; |
190 | void (*phy_isol)(struct phy_usb_instance *inst, u32 on); |
191 | void (*phy_init)(struct exynos5_usbdrd_phy *phy_drd); |
192 | unsigned int (*set_refclk)(struct phy_usb_instance *inst); |
193 | }; |
194 | |
195 | struct exynos5_usbdrd_phy_drvdata { |
196 | const struct exynos5_usbdrd_phy_config *phy_cfg; |
197 | const struct phy_ops *phy_ops; |
198 | u32 pmu_offset_usbdrd0_phy; |
199 | u32 pmu_offset_usbdrd1_phy; |
200 | bool has_common_clk_gate; |
201 | }; |
202 | |
203 | /** |
204 | * struct exynos5_usbdrd_phy - driver data for USB 3.0 PHY |
205 | * @dev: pointer to device instance of this platform device |
206 | * @reg_phy: usb phy controller register memory base |
207 | * @clk: phy clock for register access |
208 | * @pipeclk: clock for pipe3 phy |
209 | * @utmiclk: clock for utmi+ phy |
210 | * @itpclk: clock for ITP generation |
211 | * @drv_data: pointer to SoC level driver data structure |
212 | * @phys: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY |
213 | * instances each with its 'phy' and 'phy_cfg'. |
214 | * @extrefclk: frequency select settings when using 'separate |
215 | * reference clocks' for SS and HS operations |
216 | * @ref_clk: reference clock to PHY block from which PHY's |
217 | * operational clocks are derived |
218 | * @vbus: VBUS regulator for phy |
219 | * @vbus_boost: Boost regulator for VBUS present on few Exynos boards |
220 | */ |
221 | struct exynos5_usbdrd_phy { |
222 | struct device *dev; |
223 | void __iomem *reg_phy; |
224 | struct clk *clk; |
225 | struct clk *pipeclk; |
226 | struct clk *utmiclk; |
227 | struct clk *itpclk; |
228 | const struct exynos5_usbdrd_phy_drvdata *drv_data; |
229 | struct phy_usb_instance { |
230 | struct phy *phy; |
231 | u32 index; |
232 | struct regmap *reg_pmu; |
233 | u32 pmu_offset; |
234 | const struct exynos5_usbdrd_phy_config *phy_cfg; |
235 | } phys[EXYNOS5_DRDPHYS_NUM]; |
236 | u32 extrefclk; |
237 | struct clk *ref_clk; |
238 | struct regulator *vbus; |
239 | struct regulator *vbus_boost; |
240 | }; |
241 | |
242 | static inline |
243 | struct exynos5_usbdrd_phy *to_usbdrd_phy(struct phy_usb_instance *inst) |
244 | { |
245 | return container_of((inst), struct exynos5_usbdrd_phy, |
246 | phys[(inst)->index]); |
247 | } |
248 | |
249 | /* |
250 | * exynos5_rate_to_clk() converts the supplied clock rate to the value that |
251 | * can be written to the phy register. |
252 | */ |
253 | static unsigned int exynos5_rate_to_clk(unsigned long rate, u32 *reg) |
254 | { |
255 | /* EXYNOS5_FSEL_MASK */ |
256 | |
257 | switch (rate) { |
258 | case 9600 * KHZ: |
259 | *reg = EXYNOS5_FSEL_9MHZ6; |
260 | break; |
261 | case 10 * MHZ: |
262 | *reg = EXYNOS5_FSEL_10MHZ; |
263 | break; |
264 | case 12 * MHZ: |
265 | *reg = EXYNOS5_FSEL_12MHZ; |
266 | break; |
267 | case 19200 * KHZ: |
268 | *reg = EXYNOS5_FSEL_19MHZ2; |
269 | break; |
270 | case 20 * MHZ: |
271 | *reg = EXYNOS5_FSEL_20MHZ; |
272 | break; |
273 | case 24 * MHZ: |
274 | *reg = EXYNOS5_FSEL_24MHZ; |
275 | break; |
276 | case 26 * MHZ: |
277 | *reg = EXYNOS5_FSEL_26MHZ; |
278 | break; |
279 | case 50 * MHZ: |
280 | *reg = EXYNOS5_FSEL_50MHZ; |
281 | break; |
282 | default: |
283 | return -EINVAL; |
284 | } |
285 | |
286 | return 0; |
287 | } |
288 | |
289 | static void exynos5_usbdrd_phy_isol(struct phy_usb_instance *inst, |
290 | unsigned int on) |
291 | { |
292 | unsigned int val; |
293 | |
294 | if (!inst->reg_pmu) |
295 | return; |
296 | |
297 | val = on ? 0 : EXYNOS4_PHY_ENABLE; |
298 | |
299 | regmap_update_bits(map: inst->reg_pmu, reg: inst->pmu_offset, |
300 | EXYNOS4_PHY_ENABLE, val); |
301 | } |
302 | |
303 | /* |
304 | * Sets the pipe3 phy's clk as EXTREFCLK (XXTI) which is internal clock |
305 | * from clock core. Further sets multiplier values and spread spectrum |
306 | * clock settings for SuperSpeed operations. |
307 | */ |
308 | static unsigned int |
309 | exynos5_usbdrd_pipe3_set_refclk(struct phy_usb_instance *inst) |
310 | { |
311 | u32 reg; |
312 | struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); |
313 | |
314 | /* restore any previous reference clock settings */ |
315 | reg = readl(addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); |
316 | |
317 | /* Use EXTREFCLK as ref clock */ |
318 | reg &= ~PHYCLKRST_REFCLKSEL_MASK; |
319 | reg |= PHYCLKRST_REFCLKSEL_EXT_REFCLK; |
320 | |
321 | /* FSEL settings corresponding to reference clock */ |
322 | reg &= ~PHYCLKRST_FSEL_PIPE_MASK | |
323 | PHYCLKRST_MPLL_MULTIPLIER_MASK | |
324 | PHYCLKRST_SSC_REFCLKSEL_MASK; |
325 | switch (phy_drd->extrefclk) { |
326 | case EXYNOS5_FSEL_50MHZ: |
327 | reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF | |
328 | PHYCLKRST_SSC_REFCLKSEL(0x00)); |
329 | break; |
330 | case EXYNOS5_FSEL_24MHZ: |
331 | reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | |
332 | PHYCLKRST_SSC_REFCLKSEL(0x88)); |
333 | break; |
334 | case EXYNOS5_FSEL_20MHZ: |
335 | reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF | |
336 | PHYCLKRST_SSC_REFCLKSEL(0x00)); |
337 | break; |
338 | case EXYNOS5_FSEL_19MHZ2: |
339 | reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF | |
340 | PHYCLKRST_SSC_REFCLKSEL(0x88)); |
341 | break; |
342 | default: |
343 | dev_dbg(phy_drd->dev, "unsupported ref clk\n" ); |
344 | break; |
345 | } |
346 | |
347 | return reg; |
348 | } |
349 | |
350 | /* |
351 | * Sets the utmi phy's clk as EXTREFCLK (XXTI) which is internal clock |
352 | * from clock core. Further sets the FSEL values for HighSpeed operations. |
353 | */ |
354 | static unsigned int |
355 | exynos5_usbdrd_utmi_set_refclk(struct phy_usb_instance *inst) |
356 | { |
357 | u32 reg; |
358 | struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); |
359 | |
360 | /* restore any previous reference clock settings */ |
361 | reg = readl(addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); |
362 | |
363 | reg &= ~PHYCLKRST_REFCLKSEL_MASK; |
364 | reg |= PHYCLKRST_REFCLKSEL_EXT_REFCLK; |
365 | |
366 | reg &= ~PHYCLKRST_FSEL_UTMI_MASK | |
367 | PHYCLKRST_MPLL_MULTIPLIER_MASK | |
368 | PHYCLKRST_SSC_REFCLKSEL_MASK; |
369 | reg |= PHYCLKRST_FSEL(phy_drd->extrefclk); |
370 | |
371 | return reg; |
372 | } |
373 | |
374 | static void exynos5_usbdrd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd) |
375 | { |
376 | u32 reg; |
377 | |
378 | reg = readl(addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); |
379 | /* Set Tx De-Emphasis level */ |
380 | reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; |
381 | reg |= PHYPARAM1_PCS_TXDEEMPH; |
382 | writel(val: reg, addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); |
383 | |
384 | reg = readl(addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); |
385 | reg &= ~PHYTEST_POWERDOWN_SSP; |
386 | writel(val: reg, addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); |
387 | } |
388 | |
389 | static void exynos5_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd) |
390 | { |
391 | u32 reg; |
392 | |
393 | reg = readl(addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); |
394 | /* Set Loss-of-Signal Detector sensitivity */ |
395 | reg &= ~PHYPARAM0_REF_LOSLEVEL_MASK; |
396 | reg |= PHYPARAM0_REF_LOSLEVEL; |
397 | writel(val: reg, addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); |
398 | |
399 | reg = readl(addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); |
400 | /* Set Tx De-Emphasis level */ |
401 | reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; |
402 | reg |= PHYPARAM1_PCS_TXDEEMPH; |
403 | writel(val: reg, addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); |
404 | |
405 | /* UTMI Power Control */ |
406 | writel(PHYUTMI_OTGDISABLE, addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); |
407 | |
408 | reg = readl(addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); |
409 | reg &= ~PHYTEST_POWERDOWN_HSP; |
410 | writel(val: reg, addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); |
411 | } |
412 | |
413 | static int exynos5_usbdrd_phy_init(struct phy *phy) |
414 | { |
415 | int ret; |
416 | u32 reg; |
417 | struct phy_usb_instance *inst = phy_get_drvdata(phy); |
418 | struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); |
419 | |
420 | ret = clk_prepare_enable(clk: phy_drd->clk); |
421 | if (ret) |
422 | return ret; |
423 | |
424 | /* Reset USB 3.0 PHY */ |
425 | writel(val: 0x0, addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); |
426 | writel(val: 0x0, addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYRESUME); |
427 | |
428 | /* |
429 | * Setting the Frame length Adj value[6:1] to default 0x20 |
430 | * See xHCI 1.0 spec, 5.2.4 |
431 | */ |
432 | reg = LINKSYSTEM_XHCI_VERSION_CONTROL | |
433 | LINKSYSTEM_FLADJ(0x20); |
434 | writel(val: reg, addr: phy_drd->reg_phy + EXYNOS5_DRD_LINKSYSTEM); |
435 | |
436 | reg = readl(addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); |
437 | /* Select PHY CLK source */ |
438 | reg &= ~PHYPARAM0_REF_USE_PAD; |
439 | writel(val: reg, addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); |
440 | |
441 | /* This bit must be set for both HS and SS operations */ |
442 | reg = readl(addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL); |
443 | reg |= PHYUTMICLKSEL_UTMI_CLKSEL; |
444 | writel(val: reg, addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL); |
445 | |
446 | /* UTMI or PIPE3 specific init */ |
447 | inst->phy_cfg->phy_init(phy_drd); |
448 | |
449 | /* reference clock settings */ |
450 | reg = inst->phy_cfg->set_refclk(inst); |
451 | |
452 | /* Digital power supply in normal operating mode */ |
453 | reg |= PHYCLKRST_RETENABLEN | |
454 | /* Enable ref clock for SS function */ |
455 | PHYCLKRST_REF_SSP_EN | |
456 | /* Enable spread spectrum */ |
457 | PHYCLKRST_SSC_EN | |
458 | /* Power down HS Bias and PLL blocks in suspend mode */ |
459 | PHYCLKRST_COMMONONN | |
460 | /* Reset the port */ |
461 | PHYCLKRST_PORTRESET; |
462 | |
463 | writel(val: reg, addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); |
464 | |
465 | udelay(10); |
466 | |
467 | reg &= ~PHYCLKRST_PORTRESET; |
468 | writel(val: reg, addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); |
469 | |
470 | clk_disable_unprepare(clk: phy_drd->clk); |
471 | |
472 | return 0; |
473 | } |
474 | |
475 | static int exynos5_usbdrd_phy_exit(struct phy *phy) |
476 | { |
477 | int ret; |
478 | u32 reg; |
479 | struct phy_usb_instance *inst = phy_get_drvdata(phy); |
480 | struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); |
481 | |
482 | ret = clk_prepare_enable(clk: phy_drd->clk); |
483 | if (ret) |
484 | return ret; |
485 | |
486 | reg = PHYUTMI_OTGDISABLE | |
487 | PHYUTMI_FORCESUSPEND | |
488 | PHYUTMI_FORCESLEEP; |
489 | writel(val: reg, addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); |
490 | |
491 | /* Resetting the PHYCLKRST enable bits to reduce leakage current */ |
492 | reg = readl(addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); |
493 | reg &= ~(PHYCLKRST_REF_SSP_EN | |
494 | PHYCLKRST_SSC_EN | |
495 | PHYCLKRST_COMMONONN); |
496 | writel(val: reg, addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); |
497 | |
498 | /* Control PHYTEST to remove leakage current */ |
499 | reg = readl(addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); |
500 | reg |= PHYTEST_POWERDOWN_SSP | |
501 | PHYTEST_POWERDOWN_HSP; |
502 | writel(val: reg, addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); |
503 | |
504 | clk_disable_unprepare(clk: phy_drd->clk); |
505 | |
506 | return 0; |
507 | } |
508 | |
509 | static int exynos5_usbdrd_phy_power_on(struct phy *phy) |
510 | { |
511 | int ret; |
512 | struct phy_usb_instance *inst = phy_get_drvdata(phy); |
513 | struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); |
514 | |
515 | dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n" ); |
516 | |
517 | clk_prepare_enable(clk: phy_drd->ref_clk); |
518 | if (!phy_drd->drv_data->has_common_clk_gate) { |
519 | clk_prepare_enable(clk: phy_drd->pipeclk); |
520 | clk_prepare_enable(clk: phy_drd->utmiclk); |
521 | clk_prepare_enable(clk: phy_drd->itpclk); |
522 | } |
523 | |
524 | /* Enable VBUS supply */ |
525 | if (phy_drd->vbus_boost) { |
526 | ret = regulator_enable(regulator: phy_drd->vbus_boost); |
527 | if (ret) { |
528 | dev_err(phy_drd->dev, |
529 | "Failed to enable VBUS boost supply\n" ); |
530 | goto fail_vbus; |
531 | } |
532 | } |
533 | |
534 | if (phy_drd->vbus) { |
535 | ret = regulator_enable(regulator: phy_drd->vbus); |
536 | if (ret) { |
537 | dev_err(phy_drd->dev, "Failed to enable VBUS supply\n" ); |
538 | goto fail_vbus_boost; |
539 | } |
540 | } |
541 | |
542 | /* Power-on PHY*/ |
543 | inst->phy_cfg->phy_isol(inst, 0); |
544 | |
545 | return 0; |
546 | |
547 | fail_vbus_boost: |
548 | if (phy_drd->vbus_boost) |
549 | regulator_disable(regulator: phy_drd->vbus_boost); |
550 | |
551 | fail_vbus: |
552 | clk_disable_unprepare(clk: phy_drd->ref_clk); |
553 | if (!phy_drd->drv_data->has_common_clk_gate) { |
554 | clk_disable_unprepare(clk: phy_drd->itpclk); |
555 | clk_disable_unprepare(clk: phy_drd->utmiclk); |
556 | clk_disable_unprepare(clk: phy_drd->pipeclk); |
557 | } |
558 | |
559 | return ret; |
560 | } |
561 | |
562 | static int exynos5_usbdrd_phy_power_off(struct phy *phy) |
563 | { |
564 | struct phy_usb_instance *inst = phy_get_drvdata(phy); |
565 | struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); |
566 | |
567 | dev_dbg(phy_drd->dev, "Request to power_off usbdrd_phy phy\n" ); |
568 | |
569 | /* Power-off the PHY */ |
570 | inst->phy_cfg->phy_isol(inst, 1); |
571 | |
572 | /* Disable VBUS supply */ |
573 | if (phy_drd->vbus) |
574 | regulator_disable(regulator: phy_drd->vbus); |
575 | if (phy_drd->vbus_boost) |
576 | regulator_disable(regulator: phy_drd->vbus_boost); |
577 | |
578 | clk_disable_unprepare(clk: phy_drd->ref_clk); |
579 | if (!phy_drd->drv_data->has_common_clk_gate) { |
580 | clk_disable_unprepare(clk: phy_drd->itpclk); |
581 | clk_disable_unprepare(clk: phy_drd->pipeclk); |
582 | clk_disable_unprepare(clk: phy_drd->utmiclk); |
583 | } |
584 | |
585 | return 0; |
586 | } |
587 | |
588 | static int crport_handshake(struct exynos5_usbdrd_phy *phy_drd, |
589 | u32 val, u32 cmd) |
590 | { |
591 | unsigned int result; |
592 | int err; |
593 | |
594 | writel(val: val | cmd, addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); |
595 | |
596 | err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1, |
597 | result, (result & PHYREG1_CR_ACK), 1, 100); |
598 | if (err == -ETIMEDOUT) { |
599 | dev_err(phy_drd->dev, "CRPORT handshake timeout1 (0x%08x)\n" , val); |
600 | return err; |
601 | } |
602 | |
603 | writel(val, addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); |
604 | |
605 | err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1, |
606 | result, !(result & PHYREG1_CR_ACK), 1, 100); |
607 | if (err == -ETIMEDOUT) { |
608 | dev_err(phy_drd->dev, "CRPORT handshake timeout2 (0x%08x)\n" , val); |
609 | return err; |
610 | } |
611 | |
612 | return 0; |
613 | } |
614 | |
615 | static int crport_ctrl_write(struct exynos5_usbdrd_phy *phy_drd, |
616 | u32 addr, u32 data) |
617 | { |
618 | int ret; |
619 | |
620 | /* Write Address */ |
621 | writel(PHYREG0_CR_DATA_IN(addr), |
622 | addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); |
623 | ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(addr), |
624 | PHYREG0_CR_CAP_ADDR); |
625 | if (ret) |
626 | return ret; |
627 | |
628 | /* Write Data */ |
629 | writel(PHYREG0_CR_DATA_IN(data), |
630 | addr: phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); |
631 | ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(data), |
632 | PHYREG0_CR_CAP_DATA); |
633 | if (ret) |
634 | return ret; |
635 | |
636 | ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(data), |
637 | PHYREG0_CR_WRITE); |
638 | |
639 | return ret; |
640 | } |
641 | |
642 | /* |
643 | * Calibrate few PHY parameters using CR_PORT register to meet |
644 | * SuperSpeed requirements on Exynos5420 and Exynos5800 systems, |
645 | * which have 28nm USB 3.0 DRD PHY. |
646 | */ |
647 | static int exynos5420_usbdrd_phy_calibrate(struct exynos5_usbdrd_phy *phy_drd) |
648 | { |
649 | unsigned int temp; |
650 | int ret = 0; |
651 | |
652 | /* |
653 | * Change los_bias to (0x5) for 28nm PHY from a |
654 | * default value (0x0); los_level is set as default |
655 | * (0x9) as also reflected in los_level[30:26] bits |
656 | * of PHYPARAM0 register. |
657 | */ |
658 | temp = LOSLEVEL_OVRD_IN_LOS_BIAS_5420 | |
659 | LOSLEVEL_OVRD_IN_EN | |
660 | LOSLEVEL_OVRD_IN_LOS_LEVEL_DEFAULT; |
661 | ret = crport_ctrl_write(phy_drd, |
662 | EXYNOS5_DRD_PHYSS_LOSLEVEL_OVRD_IN, |
663 | data: temp); |
664 | if (ret) { |
665 | dev_err(phy_drd->dev, |
666 | "Failed setting Loss-of-Signal level for SuperSpeed\n" ); |
667 | return ret; |
668 | } |
669 | |
670 | /* |
671 | * Set tx_vboost_lvl to (0x5) for 28nm PHY Tuning, |
672 | * to raise Tx signal level from its default value of (0x4) |
673 | */ |
674 | temp = TX_VBOOSTLEVEL_OVRD_IN_VBOOST_5420; |
675 | ret = crport_ctrl_write(phy_drd, |
676 | EXYNOS5_DRD_PHYSS_TX_VBOOSTLEVEL_OVRD_IN, |
677 | data: temp); |
678 | if (ret) { |
679 | dev_err(phy_drd->dev, |
680 | "Failed setting Tx-Vboost-Level for SuperSpeed\n" ); |
681 | return ret; |
682 | } |
683 | |
684 | /* |
685 | * Set proper time to wait for RxDetect measurement, for |
686 | * desired reference clock of PHY, by tuning the CR_PORT |
687 | * register LANE0.TX_DEBUG which is internal to PHY. |
688 | * This fixes issue with few USB 3.0 devices, which are |
689 | * not detected (not even generate interrupts on the bus |
690 | * on insertion) without this change. |
691 | * e.g. Samsung SUM-TSB16S 3.0 USB drive. |
692 | */ |
693 | switch (phy_drd->extrefclk) { |
694 | case EXYNOS5_FSEL_50MHZ: |
695 | temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_48M_50M_52M; |
696 | break; |
697 | case EXYNOS5_FSEL_20MHZ: |
698 | case EXYNOS5_FSEL_19MHZ2: |
699 | temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_19M2_20M; |
700 | break; |
701 | case EXYNOS5_FSEL_24MHZ: |
702 | default: |
703 | temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_24M; |
704 | break; |
705 | } |
706 | |
707 | ret = crport_ctrl_write(phy_drd, |
708 | EXYNOS5_DRD_PHYSS_LANE0_TX_DEBUG, |
709 | data: temp); |
710 | if (ret) |
711 | dev_err(phy_drd->dev, |
712 | "Fail to set RxDet measurement time for SuperSpeed\n" ); |
713 | |
714 | return ret; |
715 | } |
716 | |
717 | static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev, |
718 | const struct of_phandle_args *args) |
719 | { |
720 | struct exynos5_usbdrd_phy *phy_drd = dev_get_drvdata(dev); |
721 | |
722 | if (WARN_ON(args->args[0] >= EXYNOS5_DRDPHYS_NUM)) |
723 | return ERR_PTR(error: -ENODEV); |
724 | |
725 | return phy_drd->phys[args->args[0]].phy; |
726 | } |
727 | |
728 | static int exynos5_usbdrd_phy_calibrate(struct phy *phy) |
729 | { |
730 | struct phy_usb_instance *inst = phy_get_drvdata(phy); |
731 | struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); |
732 | |
733 | if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) |
734 | return exynos5420_usbdrd_phy_calibrate(phy_drd); |
735 | return 0; |
736 | } |
737 | |
738 | static const struct phy_ops exynos5_usbdrd_phy_ops = { |
739 | .init = exynos5_usbdrd_phy_init, |
740 | .exit = exynos5_usbdrd_phy_exit, |
741 | .power_on = exynos5_usbdrd_phy_power_on, |
742 | .power_off = exynos5_usbdrd_phy_power_off, |
743 | .calibrate = exynos5_usbdrd_phy_calibrate, |
744 | .owner = THIS_MODULE, |
745 | }; |
746 | |
747 | static void exynos850_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd) |
748 | { |
749 | void __iomem *regs_base = phy_drd->reg_phy; |
750 | u32 reg; |
751 | |
752 | /* |
753 | * Disable HWACG (hardware auto clock gating control). This will force |
754 | * QACTIVE signal in Q-Channel interface to HIGH level, to make sure |
755 | * the PHY clock is not gated by the hardware. |
756 | */ |
757 | reg = readl(addr: regs_base + EXYNOS850_DRD_LINKCTRL); |
758 | reg |= LINKCTRL_FORCE_QACT; |
759 | writel(val: reg, addr: regs_base + EXYNOS850_DRD_LINKCTRL); |
760 | |
761 | /* Start PHY Reset (POR=high) */ |
762 | reg = readl(addr: regs_base + EXYNOS850_DRD_CLKRST); |
763 | reg |= CLKRST_PHY_SW_RST; |
764 | writel(val: reg, addr: regs_base + EXYNOS850_DRD_CLKRST); |
765 | |
766 | /* Enable UTMI+ */ |
767 | reg = readl(addr: regs_base + EXYNOS850_DRD_UTMI); |
768 | reg &= ~(UTMI_FORCE_SUSPEND | UTMI_FORCE_SLEEP | UTMI_DP_PULLDOWN | |
769 | UTMI_DM_PULLDOWN); |
770 | writel(val: reg, addr: regs_base + EXYNOS850_DRD_UTMI); |
771 | |
772 | /* Set PHY clock and control HS PHY */ |
773 | reg = readl(addr: regs_base + EXYNOS850_DRD_HSP); |
774 | reg |= HSP_EN_UTMISUSPEND | HSP_COMMONONN; |
775 | writel(val: reg, addr: regs_base + EXYNOS850_DRD_HSP); |
776 | |
777 | /* Set VBUS Valid and D+ pull-up control by VBUS pad usage */ |
778 | reg = readl(addr: regs_base + EXYNOS850_DRD_LINKCTRL); |
779 | reg |= LINKCTRL_BUS_FILTER_BYPASS(0xf); |
780 | writel(val: reg, addr: regs_base + EXYNOS850_DRD_LINKCTRL); |
781 | |
782 | reg = readl(addr: regs_base + EXYNOS850_DRD_UTMI); |
783 | reg |= UTMI_FORCE_BVALID | UTMI_FORCE_VBUSVALID; |
784 | writel(val: reg, addr: regs_base + EXYNOS850_DRD_UTMI); |
785 | |
786 | reg = readl(addr: regs_base + EXYNOS850_DRD_HSP); |
787 | reg |= HSP_VBUSVLDEXT | HSP_VBUSVLDEXTSEL; |
788 | writel(val: reg, addr: regs_base + EXYNOS850_DRD_HSP); |
789 | |
790 | /* Power up PHY analog blocks */ |
791 | reg = readl(addr: regs_base + EXYNOS850_DRD_HSP_TEST); |
792 | reg &= ~HSP_TEST_SIDDQ; |
793 | writel(val: reg, addr: regs_base + EXYNOS850_DRD_HSP_TEST); |
794 | |
795 | /* Finish PHY reset (POR=low) */ |
796 | udelay(10); /* required before doing POR=low */ |
797 | reg = readl(addr: regs_base + EXYNOS850_DRD_CLKRST); |
798 | reg &= ~(CLKRST_PHY_SW_RST | CLKRST_PORT_RST); |
799 | writel(val: reg, addr: regs_base + EXYNOS850_DRD_CLKRST); |
800 | udelay(75); /* required after POR=low for guaranteed PHY clock */ |
801 | |
802 | /* Disable single ended signal out */ |
803 | reg = readl(addr: regs_base + EXYNOS850_DRD_HSP); |
804 | reg &= ~HSP_FSV_OUT_EN; |
805 | writel(val: reg, addr: regs_base + EXYNOS850_DRD_HSP); |
806 | } |
807 | |
808 | static int exynos850_usbdrd_phy_init(struct phy *phy) |
809 | { |
810 | struct phy_usb_instance *inst = phy_get_drvdata(phy); |
811 | struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); |
812 | int ret; |
813 | |
814 | ret = clk_prepare_enable(clk: phy_drd->clk); |
815 | if (ret) |
816 | return ret; |
817 | |
818 | /* UTMI or PIPE3 specific init */ |
819 | inst->phy_cfg->phy_init(phy_drd); |
820 | |
821 | clk_disable_unprepare(clk: phy_drd->clk); |
822 | |
823 | return 0; |
824 | } |
825 | |
826 | static int exynos850_usbdrd_phy_exit(struct phy *phy) |
827 | { |
828 | struct phy_usb_instance *inst = phy_get_drvdata(phy); |
829 | struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); |
830 | void __iomem *regs_base = phy_drd->reg_phy; |
831 | u32 reg; |
832 | int ret; |
833 | |
834 | ret = clk_prepare_enable(clk: phy_drd->clk); |
835 | if (ret) |
836 | return ret; |
837 | |
838 | /* Set PHY clock and control HS PHY */ |
839 | reg = readl(addr: regs_base + EXYNOS850_DRD_UTMI); |
840 | reg &= ~(UTMI_DP_PULLDOWN | UTMI_DM_PULLDOWN); |
841 | reg |= UTMI_FORCE_SUSPEND | UTMI_FORCE_SLEEP; |
842 | writel(val: reg, addr: regs_base + EXYNOS850_DRD_UTMI); |
843 | |
844 | /* Power down PHY analog blocks */ |
845 | reg = readl(addr: regs_base + EXYNOS850_DRD_HSP_TEST); |
846 | reg |= HSP_TEST_SIDDQ; |
847 | writel(val: reg, addr: regs_base + EXYNOS850_DRD_HSP_TEST); |
848 | |
849 | /* Link reset */ |
850 | reg = readl(addr: regs_base + EXYNOS850_DRD_CLKRST); |
851 | reg |= CLKRST_LINK_SW_RST; |
852 | writel(val: reg, addr: regs_base + EXYNOS850_DRD_CLKRST); |
853 | udelay(10); /* required before doing POR=low */ |
854 | reg &= ~CLKRST_LINK_SW_RST; |
855 | writel(val: reg, addr: regs_base + EXYNOS850_DRD_CLKRST); |
856 | |
857 | clk_disable_unprepare(clk: phy_drd->clk); |
858 | |
859 | return 0; |
860 | } |
861 | |
862 | static const struct phy_ops exynos850_usbdrd_phy_ops = { |
863 | .init = exynos850_usbdrd_phy_init, |
864 | .exit = exynos850_usbdrd_phy_exit, |
865 | .power_on = exynos5_usbdrd_phy_power_on, |
866 | .power_off = exynos5_usbdrd_phy_power_off, |
867 | .owner = THIS_MODULE, |
868 | }; |
869 | |
870 | static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd) |
871 | { |
872 | unsigned long ref_rate; |
873 | int ret; |
874 | |
875 | phy_drd->clk = devm_clk_get(dev: phy_drd->dev, id: "phy" ); |
876 | if (IS_ERR(ptr: phy_drd->clk)) { |
877 | dev_err(phy_drd->dev, "Failed to get phy clock\n" ); |
878 | return PTR_ERR(ptr: phy_drd->clk); |
879 | } |
880 | |
881 | phy_drd->ref_clk = devm_clk_get(dev: phy_drd->dev, id: "ref" ); |
882 | if (IS_ERR(ptr: phy_drd->ref_clk)) { |
883 | dev_err(phy_drd->dev, "Failed to get phy reference clock\n" ); |
884 | return PTR_ERR(ptr: phy_drd->ref_clk); |
885 | } |
886 | ref_rate = clk_get_rate(clk: phy_drd->ref_clk); |
887 | |
888 | ret = exynos5_rate_to_clk(rate: ref_rate, reg: &phy_drd->extrefclk); |
889 | if (ret) { |
890 | dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n" , |
891 | ref_rate); |
892 | return ret; |
893 | } |
894 | |
895 | if (!phy_drd->drv_data->has_common_clk_gate) { |
896 | phy_drd->pipeclk = devm_clk_get(dev: phy_drd->dev, id: "phy_pipe" ); |
897 | if (IS_ERR(ptr: phy_drd->pipeclk)) { |
898 | dev_info(phy_drd->dev, |
899 | "PIPE3 phy operational clock not specified\n" ); |
900 | phy_drd->pipeclk = NULL; |
901 | } |
902 | |
903 | phy_drd->utmiclk = devm_clk_get(dev: phy_drd->dev, id: "phy_utmi" ); |
904 | if (IS_ERR(ptr: phy_drd->utmiclk)) { |
905 | dev_info(phy_drd->dev, |
906 | "UTMI phy operational clock not specified\n" ); |
907 | phy_drd->utmiclk = NULL; |
908 | } |
909 | |
910 | phy_drd->itpclk = devm_clk_get(dev: phy_drd->dev, id: "itp" ); |
911 | if (IS_ERR(ptr: phy_drd->itpclk)) { |
912 | dev_info(phy_drd->dev, |
913 | "ITP clock from main OSC not specified\n" ); |
914 | phy_drd->itpclk = NULL; |
915 | } |
916 | } |
917 | |
918 | return 0; |
919 | } |
920 | |
921 | static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { |
922 | { |
923 | .id = EXYNOS5_DRDPHY_UTMI, |
924 | .phy_isol = exynos5_usbdrd_phy_isol, |
925 | .phy_init = exynos5_usbdrd_utmi_init, |
926 | .set_refclk = exynos5_usbdrd_utmi_set_refclk, |
927 | }, |
928 | { |
929 | .id = EXYNOS5_DRDPHY_PIPE3, |
930 | .phy_isol = exynos5_usbdrd_phy_isol, |
931 | .phy_init = exynos5_usbdrd_pipe3_init, |
932 | .set_refclk = exynos5_usbdrd_pipe3_set_refclk, |
933 | }, |
934 | }; |
935 | |
936 | static const struct exynos5_usbdrd_phy_config phy_cfg_exynos850[] = { |
937 | { |
938 | .id = EXYNOS5_DRDPHY_UTMI, |
939 | .phy_isol = exynos5_usbdrd_phy_isol, |
940 | .phy_init = exynos850_usbdrd_utmi_init, |
941 | }, |
942 | }; |
943 | |
944 | static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { |
945 | .phy_cfg = phy_cfg_exynos5, |
946 | .phy_ops = &exynos5_usbdrd_phy_ops, |
947 | .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, |
948 | .pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL, |
949 | .has_common_clk_gate = true, |
950 | }; |
951 | |
952 | static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = { |
953 | .phy_cfg = phy_cfg_exynos5, |
954 | .phy_ops = &exynos5_usbdrd_phy_ops, |
955 | .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, |
956 | .has_common_clk_gate = true, |
957 | }; |
958 | |
959 | static const struct exynos5_usbdrd_phy_drvdata exynos5433_usbdrd_phy = { |
960 | .phy_cfg = phy_cfg_exynos5, |
961 | .phy_ops = &exynos5_usbdrd_phy_ops, |
962 | .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, |
963 | .pmu_offset_usbdrd1_phy = EXYNOS5433_USBHOST30_PHY_CONTROL, |
964 | .has_common_clk_gate = false, |
965 | }; |
966 | |
967 | static const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = { |
968 | .phy_cfg = phy_cfg_exynos5, |
969 | .phy_ops = &exynos5_usbdrd_phy_ops, |
970 | .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, |
971 | .has_common_clk_gate = false, |
972 | }; |
973 | |
974 | static const struct exynos5_usbdrd_phy_drvdata exynos850_usbdrd_phy = { |
975 | .phy_cfg = phy_cfg_exynos850, |
976 | .phy_ops = &exynos850_usbdrd_phy_ops, |
977 | .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, |
978 | .has_common_clk_gate = true, |
979 | }; |
980 | |
981 | static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { |
982 | { |
983 | .compatible = "samsung,exynos5250-usbdrd-phy" , |
984 | .data = &exynos5250_usbdrd_phy |
985 | }, { |
986 | .compatible = "samsung,exynos5420-usbdrd-phy" , |
987 | .data = &exynos5420_usbdrd_phy |
988 | }, { |
989 | .compatible = "samsung,exynos5433-usbdrd-phy" , |
990 | .data = &exynos5433_usbdrd_phy |
991 | }, { |
992 | .compatible = "samsung,exynos7-usbdrd-phy" , |
993 | .data = &exynos7_usbdrd_phy |
994 | }, { |
995 | .compatible = "samsung,exynos850-usbdrd-phy" , |
996 | .data = &exynos850_usbdrd_phy |
997 | }, |
998 | { }, |
999 | }; |
1000 | MODULE_DEVICE_TABLE(of, exynos5_usbdrd_phy_of_match); |
1001 | |
1002 | static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) |
1003 | { |
1004 | struct device *dev = &pdev->dev; |
1005 | struct device_node *node = dev->of_node; |
1006 | struct exynos5_usbdrd_phy *phy_drd; |
1007 | struct phy_provider *phy_provider; |
1008 | const struct exynos5_usbdrd_phy_drvdata *drv_data; |
1009 | struct regmap *reg_pmu; |
1010 | u32 pmu_offset; |
1011 | int i, ret; |
1012 | int channel; |
1013 | |
1014 | phy_drd = devm_kzalloc(dev, size: sizeof(*phy_drd), GFP_KERNEL); |
1015 | if (!phy_drd) |
1016 | return -ENOMEM; |
1017 | |
1018 | dev_set_drvdata(dev, data: phy_drd); |
1019 | phy_drd->dev = dev; |
1020 | |
1021 | phy_drd->reg_phy = devm_platform_ioremap_resource(pdev, index: 0); |
1022 | if (IS_ERR(ptr: phy_drd->reg_phy)) |
1023 | return PTR_ERR(ptr: phy_drd->reg_phy); |
1024 | |
1025 | drv_data = of_device_get_match_data(dev); |
1026 | if (!drv_data) |
1027 | return -EINVAL; |
1028 | |
1029 | phy_drd->drv_data = drv_data; |
1030 | |
1031 | ret = exynos5_usbdrd_phy_clk_handle(phy_drd); |
1032 | if (ret) { |
1033 | dev_err(dev, "Failed to initialize clocks\n" ); |
1034 | return ret; |
1035 | } |
1036 | |
1037 | reg_pmu = syscon_regmap_lookup_by_phandle(np: dev->of_node, |
1038 | property: "samsung,pmu-syscon" ); |
1039 | if (IS_ERR(ptr: reg_pmu)) { |
1040 | dev_err(dev, "Failed to lookup PMU regmap\n" ); |
1041 | return PTR_ERR(ptr: reg_pmu); |
1042 | } |
1043 | |
1044 | /* |
1045 | * Exynos5420 SoC has multiple channels for USB 3.0 PHY, with |
1046 | * each having separate power control registers. |
1047 | * 'channel' facilitates to set such registers. |
1048 | */ |
1049 | channel = of_alias_get_id(np: node, stem: "usbdrdphy" ); |
1050 | if (channel < 0) |
1051 | dev_dbg(dev, "Not a multi-controller usbdrd phy\n" ); |
1052 | |
1053 | switch (channel) { |
1054 | case 1: |
1055 | pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd1_phy; |
1056 | break; |
1057 | case 0: |
1058 | default: |
1059 | pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd0_phy; |
1060 | break; |
1061 | } |
1062 | |
1063 | /* Get Vbus regulators */ |
1064 | phy_drd->vbus = devm_regulator_get(dev, id: "vbus" ); |
1065 | if (IS_ERR(ptr: phy_drd->vbus)) { |
1066 | ret = PTR_ERR(ptr: phy_drd->vbus); |
1067 | if (ret == -EPROBE_DEFER) |
1068 | return ret; |
1069 | |
1070 | dev_warn(dev, "Failed to get VBUS supply regulator\n" ); |
1071 | phy_drd->vbus = NULL; |
1072 | } |
1073 | |
1074 | phy_drd->vbus_boost = devm_regulator_get(dev, id: "vbus-boost" ); |
1075 | if (IS_ERR(ptr: phy_drd->vbus_boost)) { |
1076 | ret = PTR_ERR(ptr: phy_drd->vbus_boost); |
1077 | if (ret == -EPROBE_DEFER) |
1078 | return ret; |
1079 | |
1080 | dev_warn(dev, "Failed to get VBUS boost supply regulator\n" ); |
1081 | phy_drd->vbus_boost = NULL; |
1082 | } |
1083 | |
1084 | dev_vdbg(dev, "Creating usbdrd_phy phy\n" ); |
1085 | |
1086 | for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) { |
1087 | struct phy *phy = devm_phy_create(dev, NULL, ops: drv_data->phy_ops); |
1088 | |
1089 | if (IS_ERR(ptr: phy)) { |
1090 | dev_err(dev, "Failed to create usbdrd_phy phy\n" ); |
1091 | return PTR_ERR(ptr: phy); |
1092 | } |
1093 | |
1094 | phy_drd->phys[i].phy = phy; |
1095 | phy_drd->phys[i].index = i; |
1096 | phy_drd->phys[i].reg_pmu = reg_pmu; |
1097 | phy_drd->phys[i].pmu_offset = pmu_offset; |
1098 | phy_drd->phys[i].phy_cfg = &drv_data->phy_cfg[i]; |
1099 | phy_set_drvdata(phy, data: &phy_drd->phys[i]); |
1100 | } |
1101 | |
1102 | phy_provider = devm_of_phy_provider_register(dev, |
1103 | exynos5_usbdrd_phy_xlate); |
1104 | if (IS_ERR(ptr: phy_provider)) { |
1105 | dev_err(phy_drd->dev, "Failed to register phy provider\n" ); |
1106 | return PTR_ERR(ptr: phy_provider); |
1107 | } |
1108 | |
1109 | return 0; |
1110 | } |
1111 | |
1112 | static struct platform_driver exynos5_usb3drd_phy = { |
1113 | .probe = exynos5_usbdrd_phy_probe, |
1114 | .driver = { |
1115 | .of_match_table = exynos5_usbdrd_phy_of_match, |
1116 | .name = "exynos5_usb3drd_phy" , |
1117 | .suppress_bind_attrs = true, |
1118 | } |
1119 | }; |
1120 | |
1121 | module_platform_driver(exynos5_usb3drd_phy); |
1122 | MODULE_DESCRIPTION("Samsung Exynos5 SoCs USB 3.0 DRD controller PHY driver" ); |
1123 | MODULE_AUTHOR("Vivek Gautam <gautam.vivek@samsung.com>" ); |
1124 | MODULE_LICENSE("GPL v2" ); |
1125 | MODULE_ALIAS("platform:exynos5_usb3drd_phy" ); |
1126 | |