Commit | Line | Data |
---|---|---|
06fb0137 KD |
1 | /* |
2 | * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4x12 support | |
3 | * | |
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | |
5 | * Author: Kamil Debski <k.debski@samsung.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | */ | |
11 | ||
12 | #include <linux/delay.h> | |
13 | #include <linux/io.h> | |
14 | #include <linux/phy/phy.h> | |
15 | #include <linux/regmap.h> | |
16 | #include "phy-samsung-usb2.h" | |
17 | ||
18 | /* Exynos USB PHY registers */ | |
19 | ||
20 | /* PHY power control */ | |
21 | #define EXYNOS_4x12_UPHYPWR 0x0 | |
22 | ||
23 | #define EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND BIT(0) | |
24 | #define EXYNOS_4x12_UPHYPWR_PHY0_PWR BIT(3) | |
25 | #define EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR BIT(4) | |
26 | #define EXYNOS_4x12_UPHYPWR_PHY0_SLEEP BIT(5) | |
27 | #define EXYNOS_4x12_UPHYPWR_PHY0 ( \ | |
28 | EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND | \ | |
29 | EXYNOS_4x12_UPHYPWR_PHY0_PWR | \ | |
30 | EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR | \ | |
31 | EXYNOS_4x12_UPHYPWR_PHY0_SLEEP) | |
32 | ||
33 | #define EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND BIT(6) | |
34 | #define EXYNOS_4x12_UPHYPWR_PHY1_PWR BIT(7) | |
35 | #define EXYNOS_4x12_UPHYPWR_PHY1_SLEEP BIT(8) | |
36 | #define EXYNOS_4x12_UPHYPWR_PHY1 ( \ | |
37 | EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND | \ | |
38 | EXYNOS_4x12_UPHYPWR_PHY1_PWR | \ | |
39 | EXYNOS_4x12_UPHYPWR_PHY1_SLEEP) | |
40 | ||
41 | #define EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND BIT(9) | |
42 | #define EXYNOS_4x12_UPHYPWR_HSIC0_PWR BIT(10) | |
43 | #define EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP BIT(11) | |
44 | #define EXYNOS_4x12_UPHYPWR_HSIC0 ( \ | |
45 | EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND | \ | |
46 | EXYNOS_4x12_UPHYPWR_HSIC0_PWR | \ | |
47 | EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP) | |
48 | ||
49 | #define EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND BIT(12) | |
50 | #define EXYNOS_4x12_UPHYPWR_HSIC1_PWR BIT(13) | |
51 | #define EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP BIT(14) | |
52 | #define EXYNOS_4x12_UPHYPWR_HSIC1 ( \ | |
53 | EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND | \ | |
54 | EXYNOS_4x12_UPHYPWR_HSIC1_PWR | \ | |
55 | EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP) | |
56 | ||
57 | /* PHY clock control */ | |
58 | #define EXYNOS_4x12_UPHYCLK 0x4 | |
59 | ||
60 | #define EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK (0x7 << 0) | |
61 | #define EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET 0 | |
62 | #define EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6 (0x0 << 0) | |
63 | #define EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ (0x1 << 0) | |
64 | #define EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) | |
65 | #define EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2 (0x3 << 0) | |
66 | #define EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ (0x4 << 0) | |
67 | #define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ (0x5 << 0) | |
68 | #define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ (0x7 << 0) | |
69 | ||
016e0d3c MS |
70 | #define EXYNOS_3250_UPHYCLK_REFCLKSEL (0x2 << 8) |
71 | ||
06fb0137 KD |
72 | #define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP BIT(3) |
73 | #define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON BIT(4) | |
74 | #define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON BIT(7) | |
75 | ||
76 | #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_MASK (0x7f << 10) | |
77 | #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_OFFSET 10 | |
78 | #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_12MHZ (0x24 << 10) | |
79 | #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_15MHZ (0x1c << 10) | |
80 | #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_16MHZ (0x1a << 10) | |
81 | #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_19MHZ2 (0x15 << 10) | |
82 | #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_20MHZ (0x14 << 10) | |
83 | ||
84 | /* PHY reset control */ | |
85 | #define EXYNOS_4x12_UPHYRST 0x8 | |
86 | ||
87 | #define EXYNOS_4x12_URSTCON_PHY0 BIT(0) | |
88 | #define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1) | |
89 | #define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2) | |
90 | #define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3) | |
57416c23 KD |
91 | /* The following bit defines are presented in the |
92 | * order taken from the Exynos4412 reference manual. | |
93 | * | |
94 | * During experiments with the hardware and debugging | |
95 | * it was determined that the hardware behaves contrary | |
96 | * to the manual. | |
97 | * | |
98 | * The following bit values were chaned accordingly to the | |
99 | * results of real hardware experiments. | |
100 | */ | |
06fb0137 | 101 | #define EXYNOS_4x12_URSTCON_PHY1 BIT(4) |
57416c23 KD |
102 | #define EXYNOS_4x12_URSTCON_HSIC0 BIT(6) |
103 | #define EXYNOS_4x12_URSTCON_HSIC1 BIT(5) | |
06fb0137 | 104 | #define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7) |
57416c23 | 105 | #define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(10) |
06fb0137 | 106 | #define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9) |
57416c23 | 107 | #define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(8) |
06fb0137 KD |
108 | |
109 | /* Isolation, configured in the power management unit */ | |
110 | #define EXYNOS_4x12_USB_ISOL_OFFSET 0x704 | |
111 | #define EXYNOS_4x12_USB_ISOL_OTG BIT(0) | |
112 | #define EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET 0x708 | |
113 | #define EXYNOS_4x12_USB_ISOL_HSIC0 BIT(0) | |
114 | #define EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET 0x70c | |
115 | #define EXYNOS_4x12_USB_ISOL_HSIC1 BIT(0) | |
116 | ||
117 | /* Mode switching SUB Device <-> Host */ | |
118 | #define EXYNOS_4x12_MODE_SWITCH_OFFSET 0x21c | |
119 | #define EXYNOS_4x12_MODE_SWITCH_MASK 1 | |
120 | #define EXYNOS_4x12_MODE_SWITCH_DEVICE 0 | |
121 | #define EXYNOS_4x12_MODE_SWITCH_HOST 1 | |
122 | ||
123 | enum exynos4x12_phy_id { | |
124 | EXYNOS4x12_DEVICE, | |
125 | EXYNOS4x12_HOST, | |
126 | EXYNOS4x12_HSIC0, | |
127 | EXYNOS4x12_HSIC1, | |
128 | EXYNOS4x12_NUM_PHYS, | |
129 | }; | |
130 | ||
131 | /* | |
132 | * exynos4x12_rate_to_clk() converts the supplied clock rate to the value that | |
133 | * can be written to the phy register. | |
134 | */ | |
135 | static int exynos4x12_rate_to_clk(unsigned long rate, u32 *reg) | |
136 | { | |
137 | /* EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK */ | |
138 | ||
139 | switch (rate) { | |
140 | case 9600 * KHZ: | |
141 | *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6; | |
142 | break; | |
143 | case 10 * MHZ: | |
144 | *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ; | |
145 | break; | |
146 | case 12 * MHZ: | |
147 | *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ; | |
148 | break; | |
149 | case 19200 * KHZ: | |
150 | *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2; | |
151 | break; | |
152 | case 20 * MHZ: | |
153 | *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ; | |
154 | break; | |
155 | case 24 * MHZ: | |
156 | *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ; | |
157 | break; | |
158 | case 50 * MHZ: | |
159 | *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ; | |
160 | break; | |
161 | default: | |
162 | return -EINVAL; | |
163 | } | |
164 | ||
165 | return 0; | |
166 | } | |
167 | ||
168 | static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst, bool on) | |
169 | { | |
170 | struct samsung_usb2_phy_driver *drv = inst->drv; | |
171 | u32 offset; | |
172 | u32 mask; | |
173 | ||
174 | switch (inst->cfg->id) { | |
175 | case EXYNOS4x12_DEVICE: | |
176 | case EXYNOS4x12_HOST: | |
177 | offset = EXYNOS_4x12_USB_ISOL_OFFSET; | |
178 | mask = EXYNOS_4x12_USB_ISOL_OTG; | |
179 | break; | |
180 | case EXYNOS4x12_HSIC0: | |
181 | offset = EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET; | |
182 | mask = EXYNOS_4x12_USB_ISOL_HSIC0; | |
183 | break; | |
184 | case EXYNOS4x12_HSIC1: | |
185 | offset = EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET; | |
186 | mask = EXYNOS_4x12_USB_ISOL_HSIC1; | |
187 | break; | |
188 | default: | |
189 | return; | |
190 | }; | |
191 | ||
192 | regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); | |
193 | } | |
194 | ||
195 | static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst) | |
196 | { | |
197 | struct samsung_usb2_phy_driver *drv = inst->drv; | |
198 | u32 clk; | |
199 | ||
200 | clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK); | |
201 | clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK; | |
016e0d3c MS |
202 | |
203 | if (drv->cfg->has_refclk_sel) | |
204 | clk = EXYNOS_3250_UPHYCLK_REFCLKSEL; | |
205 | ||
06fb0137 | 206 | clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET; |
57416c23 | 207 | clk |= EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON; |
06fb0137 KD |
208 | writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK); |
209 | } | |
210 | ||
211 | static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) | |
212 | { | |
213 | struct samsung_usb2_phy_driver *drv = inst->drv; | |
214 | u32 rstbits = 0; | |
215 | u32 phypwr = 0; | |
216 | u32 rst; | |
217 | u32 pwr; | |
06fb0137 KD |
218 | |
219 | switch (inst->cfg->id) { | |
220 | case EXYNOS4x12_DEVICE: | |
221 | phypwr = EXYNOS_4x12_UPHYPWR_PHY0; | |
222 | rstbits = EXYNOS_4x12_URSTCON_PHY0; | |
06fb0137 KD |
223 | break; |
224 | case EXYNOS4x12_HOST: | |
225 | phypwr = EXYNOS_4x12_UPHYPWR_PHY1; | |
57416c23 KD |
226 | rstbits = EXYNOS_4x12_URSTCON_HOST_PHY | |
227 | EXYNOS_4x12_URSTCON_PHY1 | | |
228 | EXYNOS_4x12_URSTCON_HOST_LINK_P0; | |
06fb0137 KD |
229 | break; |
230 | case EXYNOS4x12_HSIC0: | |
231 | phypwr = EXYNOS_4x12_UPHYPWR_HSIC0; | |
57416c23 KD |
232 | rstbits = EXYNOS_4x12_URSTCON_HSIC0 | |
233 | EXYNOS_4x12_URSTCON_HOST_LINK_P1; | |
06fb0137 KD |
234 | break; |
235 | case EXYNOS4x12_HSIC1: | |
236 | phypwr = EXYNOS_4x12_UPHYPWR_HSIC1; | |
237 | rstbits = EXYNOS_4x12_URSTCON_HSIC1 | | |
238 | EXYNOS_4x12_URSTCON_HOST_LINK_P1; | |
239 | break; | |
240 | }; | |
241 | ||
242 | if (on) { | |
06fb0137 KD |
243 | pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); |
244 | pwr &= ~phypwr; | |
245 | writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); | |
246 | ||
247 | rst = readl(drv->reg_phy + EXYNOS_4x12_UPHYRST); | |
248 | rst |= rstbits; | |
249 | writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST); | |
250 | udelay(10); | |
251 | rst &= ~rstbits; | |
252 | writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST); | |
253 | /* The following delay is necessary for the reset sequence to be | |
254 | * completed */ | |
255 | udelay(80); | |
256 | } else { | |
257 | pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); | |
258 | pwr |= phypwr; | |
259 | writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); | |
260 | } | |
261 | } | |
262 | ||
57416c23 | 263 | static void exynos4x12_power_on_int(struct samsung_usb2_phy_instance *inst) |
06fb0137 | 264 | { |
57416c23 KD |
265 | if (inst->int_cnt++ > 0) |
266 | return; | |
06fb0137 | 267 | |
06fb0137 | 268 | exynos4x12_setup_clk(inst); |
06fb0137 | 269 | exynos4x12_isol(inst, 0); |
57416c23 KD |
270 | exynos4x12_phy_pwr(inst, 1); |
271 | } | |
272 | ||
273 | static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst) | |
274 | { | |
275 | struct samsung_usb2_phy_driver *drv = inst->drv; | |
276 | ||
277 | if (inst->ext_cnt++ > 0) | |
278 | return 0; | |
06fb0137 | 279 | |
57416c23 KD |
280 | if (inst->cfg->id == EXYNOS4x12_HOST) { |
281 | regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, | |
282 | EXYNOS_4x12_MODE_SWITCH_MASK, | |
283 | EXYNOS_4x12_MODE_SWITCH_HOST); | |
284 | exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); | |
06fb0137 KD |
285 | } |
286 | ||
016e0d3c | 287 | if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch) |
57416c23 KD |
288 | regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, |
289 | EXYNOS_4x12_MODE_SWITCH_MASK, | |
290 | EXYNOS_4x12_MODE_SWITCH_DEVICE); | |
291 | ||
292 | if (inst->cfg->id == EXYNOS4x12_HSIC0 || | |
293 | inst->cfg->id == EXYNOS4x12_HSIC1) { | |
294 | exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); | |
295 | exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_HOST]); | |
296 | } | |
297 | ||
298 | exynos4x12_power_on_int(inst); | |
299 | ||
06fb0137 KD |
300 | return 0; |
301 | } | |
302 | ||
57416c23 | 303 | static void exynos4x12_power_off_int(struct samsung_usb2_phy_instance *inst) |
06fb0137 | 304 | { |
57416c23 KD |
305 | if (inst->int_cnt-- > 1) |
306 | return; | |
06fb0137 | 307 | |
06fb0137 KD |
308 | exynos4x12_isol(inst, 1); |
309 | exynos4x12_phy_pwr(inst, 0); | |
57416c23 | 310 | } |
06fb0137 | 311 | |
57416c23 KD |
312 | static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst) |
313 | { | |
314 | struct samsung_usb2_phy_driver *drv = inst->drv; | |
315 | ||
316 | if (inst->ext_cnt-- > 1) | |
317 | return 0; | |
318 | ||
016e0d3c | 319 | if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch) |
57416c23 KD |
320 | regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, |
321 | EXYNOS_4x12_MODE_SWITCH_MASK, | |
322 | EXYNOS_4x12_MODE_SWITCH_HOST); | |
323 | ||
324 | if (inst->cfg->id == EXYNOS4x12_HOST) | |
325 | exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); | |
326 | ||
327 | if (inst->cfg->id == EXYNOS4x12_HSIC0 || | |
328 | inst->cfg->id == EXYNOS4x12_HSIC1) { | |
329 | exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); | |
330 | exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_HOST]); | |
06fb0137 KD |
331 | } |
332 | ||
57416c23 KD |
333 | exynos4x12_power_off_int(inst); |
334 | ||
06fb0137 KD |
335 | return 0; |
336 | } | |
337 | ||
338 | ||
339 | static const struct samsung_usb2_common_phy exynos4x12_phys[] = { | |
340 | { | |
341 | .label = "device", | |
342 | .id = EXYNOS4x12_DEVICE, | |
343 | .power_on = exynos4x12_power_on, | |
344 | .power_off = exynos4x12_power_off, | |
345 | }, | |
346 | { | |
347 | .label = "host", | |
348 | .id = EXYNOS4x12_HOST, | |
349 | .power_on = exynos4x12_power_on, | |
350 | .power_off = exynos4x12_power_off, | |
351 | }, | |
352 | { | |
353 | .label = "hsic0", | |
354 | .id = EXYNOS4x12_HSIC0, | |
355 | .power_on = exynos4x12_power_on, | |
356 | .power_off = exynos4x12_power_off, | |
357 | }, | |
358 | { | |
359 | .label = "hsic1", | |
360 | .id = EXYNOS4x12_HSIC1, | |
361 | .power_on = exynos4x12_power_on, | |
362 | .power_off = exynos4x12_power_off, | |
363 | }, | |
06fb0137 KD |
364 | }; |
365 | ||
016e0d3c MS |
366 | const struct samsung_usb2_phy_config exynos3250_usb2_phy_config = { |
367 | .has_refclk_sel = 1, | |
368 | .num_phys = 1, | |
369 | .phys = exynos4x12_phys, | |
370 | .rate_to_clk = exynos4x12_rate_to_clk, | |
371 | }; | |
372 | ||
06fb0137 KD |
373 | const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = { |
374 | .has_mode_switch = 1, | |
375 | .num_phys = EXYNOS4x12_NUM_PHYS, | |
376 | .phys = exynos4x12_phys, | |
377 | .rate_to_clk = exynos4x12_rate_to_clk, | |
378 | }; |