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 | ||
70 | #define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP BIT(3) | |
71 | #define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON BIT(4) | |
72 | #define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON BIT(7) | |
73 | ||
74 | #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_MASK (0x7f << 10) | |
75 | #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_OFFSET 10 | |
76 | #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_12MHZ (0x24 << 10) | |
77 | #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_15MHZ (0x1c << 10) | |
78 | #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_16MHZ (0x1a << 10) | |
79 | #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_19MHZ2 (0x15 << 10) | |
80 | #define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_20MHZ (0x14 << 10) | |
81 | ||
82 | /* PHY reset control */ | |
83 | #define EXYNOS_4x12_UPHYRST 0x8 | |
84 | ||
85 | #define EXYNOS_4x12_URSTCON_PHY0 BIT(0) | |
86 | #define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1) | |
87 | #define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2) | |
88 | #define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3) | |
57416c23 KD |
89 | /* The following bit defines are presented in the |
90 | * order taken from the Exynos4412 reference manual. | |
91 | * | |
92 | * During experiments with the hardware and debugging | |
93 | * it was determined that the hardware behaves contrary | |
94 | * to the manual. | |
95 | * | |
96 | * The following bit values were chaned accordingly to the | |
97 | * results of real hardware experiments. | |
98 | */ | |
06fb0137 | 99 | #define EXYNOS_4x12_URSTCON_PHY1 BIT(4) |
57416c23 KD |
100 | #define EXYNOS_4x12_URSTCON_HSIC0 BIT(6) |
101 | #define EXYNOS_4x12_URSTCON_HSIC1 BIT(5) | |
06fb0137 | 102 | #define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7) |
57416c23 | 103 | #define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(10) |
06fb0137 | 104 | #define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9) |
57416c23 | 105 | #define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(8) |
06fb0137 KD |
106 | |
107 | /* Isolation, configured in the power management unit */ | |
108 | #define EXYNOS_4x12_USB_ISOL_OFFSET 0x704 | |
109 | #define EXYNOS_4x12_USB_ISOL_OTG BIT(0) | |
110 | #define EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET 0x708 | |
111 | #define EXYNOS_4x12_USB_ISOL_HSIC0 BIT(0) | |
112 | #define EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET 0x70c | |
113 | #define EXYNOS_4x12_USB_ISOL_HSIC1 BIT(0) | |
114 | ||
115 | /* Mode switching SUB Device <-> Host */ | |
116 | #define EXYNOS_4x12_MODE_SWITCH_OFFSET 0x21c | |
117 | #define EXYNOS_4x12_MODE_SWITCH_MASK 1 | |
118 | #define EXYNOS_4x12_MODE_SWITCH_DEVICE 0 | |
119 | #define EXYNOS_4x12_MODE_SWITCH_HOST 1 | |
120 | ||
121 | enum exynos4x12_phy_id { | |
122 | EXYNOS4x12_DEVICE, | |
123 | EXYNOS4x12_HOST, | |
124 | EXYNOS4x12_HSIC0, | |
125 | EXYNOS4x12_HSIC1, | |
126 | EXYNOS4x12_NUM_PHYS, | |
127 | }; | |
128 | ||
129 | /* | |
130 | * exynos4x12_rate_to_clk() converts the supplied clock rate to the value that | |
131 | * can be written to the phy register. | |
132 | */ | |
133 | static int exynos4x12_rate_to_clk(unsigned long rate, u32 *reg) | |
134 | { | |
135 | /* EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK */ | |
136 | ||
137 | switch (rate) { | |
138 | case 9600 * KHZ: | |
139 | *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6; | |
140 | break; | |
141 | case 10 * MHZ: | |
142 | *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ; | |
143 | break; | |
144 | case 12 * MHZ: | |
145 | *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ; | |
146 | break; | |
147 | case 19200 * KHZ: | |
148 | *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2; | |
149 | break; | |
150 | case 20 * MHZ: | |
151 | *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ; | |
152 | break; | |
153 | case 24 * MHZ: | |
154 | *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ; | |
155 | break; | |
156 | case 50 * MHZ: | |
157 | *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ; | |
158 | break; | |
159 | default: | |
160 | return -EINVAL; | |
161 | } | |
162 | ||
163 | return 0; | |
164 | } | |
165 | ||
166 | static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst, bool on) | |
167 | { | |
168 | struct samsung_usb2_phy_driver *drv = inst->drv; | |
169 | u32 offset; | |
170 | u32 mask; | |
171 | ||
172 | switch (inst->cfg->id) { | |
173 | case EXYNOS4x12_DEVICE: | |
174 | case EXYNOS4x12_HOST: | |
175 | offset = EXYNOS_4x12_USB_ISOL_OFFSET; | |
176 | mask = EXYNOS_4x12_USB_ISOL_OTG; | |
177 | break; | |
178 | case EXYNOS4x12_HSIC0: | |
179 | offset = EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET; | |
180 | mask = EXYNOS_4x12_USB_ISOL_HSIC0; | |
181 | break; | |
182 | case EXYNOS4x12_HSIC1: | |
183 | offset = EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET; | |
184 | mask = EXYNOS_4x12_USB_ISOL_HSIC1; | |
185 | break; | |
186 | default: | |
187 | return; | |
188 | }; | |
189 | ||
190 | regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); | |
191 | } | |
192 | ||
193 | static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst) | |
194 | { | |
195 | struct samsung_usb2_phy_driver *drv = inst->drv; | |
196 | u32 clk; | |
197 | ||
198 | clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK); | |
199 | clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK; | |
200 | clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET; | |
57416c23 | 201 | clk |= EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON; |
06fb0137 KD |
202 | writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK); |
203 | } | |
204 | ||
205 | static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) | |
206 | { | |
207 | struct samsung_usb2_phy_driver *drv = inst->drv; | |
208 | u32 rstbits = 0; | |
209 | u32 phypwr = 0; | |
210 | u32 rst; | |
211 | u32 pwr; | |
06fb0137 KD |
212 | |
213 | switch (inst->cfg->id) { | |
214 | case EXYNOS4x12_DEVICE: | |
215 | phypwr = EXYNOS_4x12_UPHYPWR_PHY0; | |
216 | rstbits = EXYNOS_4x12_URSTCON_PHY0; | |
06fb0137 KD |
217 | break; |
218 | case EXYNOS4x12_HOST: | |
219 | phypwr = EXYNOS_4x12_UPHYPWR_PHY1; | |
57416c23 KD |
220 | rstbits = EXYNOS_4x12_URSTCON_HOST_PHY | |
221 | EXYNOS_4x12_URSTCON_PHY1 | | |
222 | EXYNOS_4x12_URSTCON_HOST_LINK_P0; | |
06fb0137 KD |
223 | break; |
224 | case EXYNOS4x12_HSIC0: | |
225 | phypwr = EXYNOS_4x12_UPHYPWR_HSIC0; | |
57416c23 KD |
226 | rstbits = EXYNOS_4x12_URSTCON_HSIC0 | |
227 | EXYNOS_4x12_URSTCON_HOST_LINK_P1; | |
06fb0137 KD |
228 | break; |
229 | case EXYNOS4x12_HSIC1: | |
230 | phypwr = EXYNOS_4x12_UPHYPWR_HSIC1; | |
231 | rstbits = EXYNOS_4x12_URSTCON_HSIC1 | | |
232 | EXYNOS_4x12_URSTCON_HOST_LINK_P1; | |
233 | break; | |
234 | }; | |
235 | ||
236 | if (on) { | |
06fb0137 KD |
237 | pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); |
238 | pwr &= ~phypwr; | |
239 | writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); | |
240 | ||
241 | rst = readl(drv->reg_phy + EXYNOS_4x12_UPHYRST); | |
242 | rst |= rstbits; | |
243 | writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST); | |
244 | udelay(10); | |
245 | rst &= ~rstbits; | |
246 | writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST); | |
247 | /* The following delay is necessary for the reset sequence to be | |
248 | * completed */ | |
249 | udelay(80); | |
250 | } else { | |
251 | pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); | |
252 | pwr |= phypwr; | |
253 | writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); | |
254 | } | |
255 | } | |
256 | ||
57416c23 | 257 | static void exynos4x12_power_on_int(struct samsung_usb2_phy_instance *inst) |
06fb0137 | 258 | { |
57416c23 KD |
259 | if (inst->int_cnt++ > 0) |
260 | return; | |
06fb0137 | 261 | |
06fb0137 | 262 | exynos4x12_setup_clk(inst); |
06fb0137 | 263 | exynos4x12_isol(inst, 0); |
57416c23 KD |
264 | exynos4x12_phy_pwr(inst, 1); |
265 | } | |
266 | ||
267 | static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst) | |
268 | { | |
269 | struct samsung_usb2_phy_driver *drv = inst->drv; | |
270 | ||
271 | if (inst->ext_cnt++ > 0) | |
272 | return 0; | |
06fb0137 | 273 | |
57416c23 KD |
274 | if (inst->cfg->id == EXYNOS4x12_HOST) { |
275 | regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, | |
276 | EXYNOS_4x12_MODE_SWITCH_MASK, | |
277 | EXYNOS_4x12_MODE_SWITCH_HOST); | |
278 | exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); | |
06fb0137 KD |
279 | } |
280 | ||
57416c23 KD |
281 | if (inst->cfg->id == EXYNOS4x12_DEVICE) |
282 | regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, | |
283 | EXYNOS_4x12_MODE_SWITCH_MASK, | |
284 | EXYNOS_4x12_MODE_SWITCH_DEVICE); | |
285 | ||
286 | if (inst->cfg->id == EXYNOS4x12_HSIC0 || | |
287 | inst->cfg->id == EXYNOS4x12_HSIC1) { | |
288 | exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); | |
289 | exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_HOST]); | |
290 | } | |
291 | ||
292 | exynos4x12_power_on_int(inst); | |
293 | ||
06fb0137 KD |
294 | return 0; |
295 | } | |
296 | ||
57416c23 | 297 | static void exynos4x12_power_off_int(struct samsung_usb2_phy_instance *inst) |
06fb0137 | 298 | { |
57416c23 KD |
299 | if (inst->int_cnt-- > 1) |
300 | return; | |
06fb0137 | 301 | |
06fb0137 KD |
302 | exynos4x12_isol(inst, 1); |
303 | exynos4x12_phy_pwr(inst, 0); | |
57416c23 | 304 | } |
06fb0137 | 305 | |
57416c23 KD |
306 | static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst) |
307 | { | |
308 | struct samsung_usb2_phy_driver *drv = inst->drv; | |
309 | ||
310 | if (inst->ext_cnt-- > 1) | |
311 | return 0; | |
312 | ||
313 | if (inst->cfg->id == EXYNOS4x12_DEVICE) | |
314 | regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, | |
315 | EXYNOS_4x12_MODE_SWITCH_MASK, | |
316 | EXYNOS_4x12_MODE_SWITCH_HOST); | |
317 | ||
318 | if (inst->cfg->id == EXYNOS4x12_HOST) | |
319 | exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); | |
320 | ||
321 | if (inst->cfg->id == EXYNOS4x12_HSIC0 || | |
322 | inst->cfg->id == EXYNOS4x12_HSIC1) { | |
323 | exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); | |
324 | exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_HOST]); | |
06fb0137 KD |
325 | } |
326 | ||
57416c23 KD |
327 | exynos4x12_power_off_int(inst); |
328 | ||
06fb0137 KD |
329 | return 0; |
330 | } | |
331 | ||
332 | ||
333 | static const struct samsung_usb2_common_phy exynos4x12_phys[] = { | |
334 | { | |
335 | .label = "device", | |
336 | .id = EXYNOS4x12_DEVICE, | |
337 | .power_on = exynos4x12_power_on, | |
338 | .power_off = exynos4x12_power_off, | |
339 | }, | |
340 | { | |
341 | .label = "host", | |
342 | .id = EXYNOS4x12_HOST, | |
343 | .power_on = exynos4x12_power_on, | |
344 | .power_off = exynos4x12_power_off, | |
345 | }, | |
346 | { | |
347 | .label = "hsic0", | |
348 | .id = EXYNOS4x12_HSIC0, | |
349 | .power_on = exynos4x12_power_on, | |
350 | .power_off = exynos4x12_power_off, | |
351 | }, | |
352 | { | |
353 | .label = "hsic1", | |
354 | .id = EXYNOS4x12_HSIC1, | |
355 | .power_on = exynos4x12_power_on, | |
356 | .power_off = exynos4x12_power_off, | |
357 | }, | |
358 | {}, | |
359 | }; | |
360 | ||
361 | const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = { | |
362 | .has_mode_switch = 1, | |
363 | .num_phys = EXYNOS4x12_NUM_PHYS, | |
364 | .phys = exynos4x12_phys, | |
365 | .rate_to_clk = exynos4x12_rate_to_clk, | |
366 | }; |