]>
Commit | Line | Data |
---|---|---|
457c8996 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
ce6bc922 ML |
2 | /* |
3 | * USB block power/access management abstraction. | |
4 | * | |
5 | * Au1000+: The OHCI block control register is at the far end of the OHCI memory | |
6 | * area. Au1550 has OHCI on different base address. No need to handle | |
7 | * UDC here. | |
8 | * Au1200: one register to control access and clocks to O/EHCI, UDC and OTG | |
9 | * as well as the PHY for EHCI and UDC. | |
10 | * | |
11 | */ | |
12 | ||
3feae784 | 13 | #include <linux/clk.h> |
26dd3e4f | 14 | #include <linux/export.h> |
ce6bc922 ML |
15 | #include <linux/init.h> |
16 | #include <linux/io.h> | |
ce6bc922 ML |
17 | #include <linux/spinlock.h> |
18 | #include <linux/syscore_ops.h> | |
8ff374b9 | 19 | #include <asm/cpu.h> |
ce6bc922 ML |
20 | #include <asm/mach-au1x00/au1000.h> |
21 | ||
22 | /* control register offsets */ | |
23 | #define AU1000_OHCICFG 0x7fffc | |
24 | #define AU1550_OHCICFG 0x07ffc | |
25 | #define AU1200_USBCFG 0x04 | |
26 | ||
27 | /* Au1000 USB block config bits */ | |
28 | #define USBHEN_RD (1 << 4) /* OHCI reset-done indicator */ | |
29 | #define USBHEN_CE (1 << 3) /* OHCI block clock enable */ | |
30 | #define USBHEN_E (1 << 2) /* OHCI block enable */ | |
31 | #define USBHEN_C (1 << 1) /* OHCI block coherency bit */ | |
32 | #define USBHEN_BE (1 << 0) /* OHCI Big-Endian */ | |
33 | ||
34 | /* Au1200 USB config bits */ | |
35 | #define USBCFG_PFEN (1 << 31) /* prefetch enable (undoc) */ | |
36 | #define USBCFG_RDCOMB (1 << 30) /* read combining (undoc) */ | |
37 | #define USBCFG_UNKNOWN (5 << 20) /* unknown, leave this way */ | |
38 | #define USBCFG_SSD (1 << 23) /* serial short detect en */ | |
39 | #define USBCFG_PPE (1 << 19) /* HS PHY PLL */ | |
40 | #define USBCFG_UCE (1 << 18) /* UDC clock enable */ | |
41 | #define USBCFG_ECE (1 << 17) /* EHCI clock enable */ | |
42 | #define USBCFG_OCE (1 << 16) /* OHCI clock enable */ | |
43 | #define USBCFG_FLA(x) (((x) & 0x3f) << 8) | |
44 | #define USBCFG_UCAM (1 << 7) /* coherent access (undoc) */ | |
45 | #define USBCFG_GME (1 << 6) /* OTG mem access */ | |
46 | #define USBCFG_DBE (1 << 5) /* UDC busmaster enable */ | |
47 | #define USBCFG_DME (1 << 4) /* UDC mem enable */ | |
48 | #define USBCFG_EBE (1 << 3) /* EHCI busmaster enable */ | |
49 | #define USBCFG_EME (1 << 2) /* EHCI mem enable */ | |
50 | #define USBCFG_OBE (1 << 1) /* OHCI busmaster enable */ | |
51 | #define USBCFG_OME (1 << 0) /* OHCI mem enable */ | |
52 | #define USBCFG_INIT_AU1200 (USBCFG_PFEN | USBCFG_RDCOMB | USBCFG_UNKNOWN |\ | |
53 | USBCFG_SSD | USBCFG_FLA(0x20) | USBCFG_UCAM | \ | |
54 | USBCFG_GME | USBCFG_DBE | USBCFG_DME | \ | |
55 | USBCFG_EBE | USBCFG_EME | USBCFG_OBE | \ | |
56 | USBCFG_OME) | |
57 | ||
809f36c6 ML |
58 | /* Au1300 USB config registers */ |
59 | #define USB_DWC_CTRL1 0x00 | |
60 | #define USB_DWC_CTRL2 0x04 | |
61 | #define USB_VBUS_TIMER 0x10 | |
62 | #define USB_SBUS_CTRL 0x14 | |
63 | #define USB_MSR_ERR 0x18 | |
64 | #define USB_DWC_CTRL3 0x1C | |
65 | #define USB_DWC_CTRL4 0x20 | |
66 | #define USB_OTG_STATUS 0x28 | |
67 | #define USB_DWC_CTRL5 0x2C | |
68 | #define USB_DWC_CTRL6 0x30 | |
69 | #define USB_DWC_CTRL7 0x34 | |
70 | #define USB_PHY_STATUS 0xC0 | |
71 | #define USB_INT_STATUS 0xC4 | |
72 | #define USB_INT_ENABLE 0xC8 | |
73 | ||
74 | #define USB_DWC_CTRL1_OTGD 0x04 /* set to DISable OTG */ | |
75 | #define USB_DWC_CTRL1_HSTRS 0x02 /* set to ENable EHCI */ | |
76 | #define USB_DWC_CTRL1_DCRS 0x01 /* set to ENable UDC */ | |
77 | ||
78 | #define USB_DWC_CTRL2_PHY1RS 0x04 /* set to enable PHY1 */ | |
79 | #define USB_DWC_CTRL2_PHY0RS 0x02 /* set to enable PHY0 */ | |
80 | #define USB_DWC_CTRL2_PHYRS 0x01 /* set to enable PHY */ | |
81 | ||
82 | #define USB_DWC_CTRL3_OHCI1_CKEN (1 << 19) | |
83 | #define USB_DWC_CTRL3_OHCI0_CKEN (1 << 18) | |
84 | #define USB_DWC_CTRL3_EHCI0_CKEN (1 << 17) | |
85 | #define USB_DWC_CTRL3_OTG0_CKEN (1 << 16) | |
86 | ||
87 | #define USB_SBUS_CTRL_SBCA 0x04 /* coherent access */ | |
88 | ||
89 | #define USB_INTEN_FORCE 0x20 | |
90 | #define USB_INTEN_PHY 0x10 | |
91 | #define USB_INTEN_UDC 0x08 | |
92 | #define USB_INTEN_EHCI 0x04 | |
93 | #define USB_INTEN_OHCI1 0x02 | |
94 | #define USB_INTEN_OHCI0 0x01 | |
ce6bc922 ML |
95 | |
96 | static DEFINE_SPINLOCK(alchemy_usb_lock); | |
97 | ||
809f36c6 ML |
98 | static inline void __au1300_usb_phyctl(void __iomem *base, int enable) |
99 | { | |
100 | unsigned long r, s; | |
101 | ||
102 | r = __raw_readl(base + USB_DWC_CTRL2); | |
103 | s = __raw_readl(base + USB_DWC_CTRL3); | |
104 | ||
105 | s &= USB_DWC_CTRL3_OHCI1_CKEN | USB_DWC_CTRL3_OHCI0_CKEN | | |
106 | USB_DWC_CTRL3_EHCI0_CKEN | USB_DWC_CTRL3_OTG0_CKEN; | |
107 | ||
108 | if (enable) { | |
109 | /* simply enable all PHYs */ | |
110 | r |= USB_DWC_CTRL2_PHY1RS | USB_DWC_CTRL2_PHY0RS | | |
111 | USB_DWC_CTRL2_PHYRS; | |
112 | __raw_writel(r, base + USB_DWC_CTRL2); | |
113 | wmb(); | |
114 | } else if (!s) { | |
115 | /* no USB block active, do disable all PHYs */ | |
116 | r &= ~(USB_DWC_CTRL2_PHY1RS | USB_DWC_CTRL2_PHY0RS | | |
117 | USB_DWC_CTRL2_PHYRS); | |
118 | __raw_writel(r, base + USB_DWC_CTRL2); | |
119 | wmb(); | |
120 | } | |
121 | } | |
122 | ||
123 | static inline void __au1300_ohci_control(void __iomem *base, int enable, int id) | |
124 | { | |
125 | unsigned long r; | |
126 | ||
127 | if (enable) { | |
70342287 | 128 | __raw_writel(1, base + USB_DWC_CTRL7); /* start OHCI clock */ |
809f36c6 ML |
129 | wmb(); |
130 | ||
131 | r = __raw_readl(base + USB_DWC_CTRL3); /* enable OHCI block */ | |
132 | r |= (id == 0) ? USB_DWC_CTRL3_OHCI0_CKEN | |
133 | : USB_DWC_CTRL3_OHCI1_CKEN; | |
134 | __raw_writel(r, base + USB_DWC_CTRL3); | |
135 | wmb(); | |
136 | ||
137 | __au1300_usb_phyctl(base, enable); /* power up the PHYs */ | |
138 | ||
139 | r = __raw_readl(base + USB_INT_ENABLE); | |
140 | r |= (id == 0) ? USB_INTEN_OHCI0 : USB_INTEN_OHCI1; | |
141 | __raw_writel(r, base + USB_INT_ENABLE); | |
142 | wmb(); | |
143 | ||
144 | /* reset the OHCI start clock bit */ | |
145 | __raw_writel(0, base + USB_DWC_CTRL7); | |
146 | wmb(); | |
147 | } else { | |
148 | r = __raw_readl(base + USB_INT_ENABLE); | |
149 | r &= ~((id == 0) ? USB_INTEN_OHCI0 : USB_INTEN_OHCI1); | |
150 | __raw_writel(r, base + USB_INT_ENABLE); | |
151 | wmb(); | |
152 | ||
153 | r = __raw_readl(base + USB_DWC_CTRL3); | |
154 | r &= ~((id == 0) ? USB_DWC_CTRL3_OHCI0_CKEN | |
155 | : USB_DWC_CTRL3_OHCI1_CKEN); | |
156 | __raw_writel(r, base + USB_DWC_CTRL3); | |
157 | wmb(); | |
158 | ||
159 | __au1300_usb_phyctl(base, enable); | |
160 | } | |
161 | } | |
162 | ||
163 | static inline void __au1300_ehci_control(void __iomem *base, int enable) | |
164 | { | |
165 | unsigned long r; | |
166 | ||
167 | if (enable) { | |
168 | r = __raw_readl(base + USB_DWC_CTRL3); | |
169 | r |= USB_DWC_CTRL3_EHCI0_CKEN; | |
170 | __raw_writel(r, base + USB_DWC_CTRL3); | |
171 | wmb(); | |
172 | ||
173 | r = __raw_readl(base + USB_DWC_CTRL1); | |
174 | r |= USB_DWC_CTRL1_HSTRS; | |
175 | __raw_writel(r, base + USB_DWC_CTRL1); | |
176 | wmb(); | |
177 | ||
178 | __au1300_usb_phyctl(base, enable); | |
179 | ||
180 | r = __raw_readl(base + USB_INT_ENABLE); | |
181 | r |= USB_INTEN_EHCI; | |
182 | __raw_writel(r, base + USB_INT_ENABLE); | |
183 | wmb(); | |
184 | } else { | |
185 | r = __raw_readl(base + USB_INT_ENABLE); | |
186 | r &= ~USB_INTEN_EHCI; | |
187 | __raw_writel(r, base + USB_INT_ENABLE); | |
188 | wmb(); | |
189 | ||
190 | r = __raw_readl(base + USB_DWC_CTRL1); | |
191 | r &= ~USB_DWC_CTRL1_HSTRS; | |
192 | __raw_writel(r, base + USB_DWC_CTRL1); | |
193 | wmb(); | |
194 | ||
195 | r = __raw_readl(base + USB_DWC_CTRL3); | |
196 | r &= ~USB_DWC_CTRL3_EHCI0_CKEN; | |
197 | __raw_writel(r, base + USB_DWC_CTRL3); | |
198 | wmb(); | |
199 | ||
200 | __au1300_usb_phyctl(base, enable); | |
201 | } | |
202 | } | |
203 | ||
204 | static inline void __au1300_udc_control(void __iomem *base, int enable) | |
205 | { | |
206 | unsigned long r; | |
207 | ||
208 | if (enable) { | |
209 | r = __raw_readl(base + USB_DWC_CTRL1); | |
210 | r |= USB_DWC_CTRL1_DCRS; | |
211 | __raw_writel(r, base + USB_DWC_CTRL1); | |
212 | wmb(); | |
213 | ||
214 | __au1300_usb_phyctl(base, enable); | |
215 | ||
216 | r = __raw_readl(base + USB_INT_ENABLE); | |
217 | r |= USB_INTEN_UDC; | |
218 | __raw_writel(r, base + USB_INT_ENABLE); | |
219 | wmb(); | |
220 | } else { | |
221 | r = __raw_readl(base + USB_INT_ENABLE); | |
222 | r &= ~USB_INTEN_UDC; | |
223 | __raw_writel(r, base + USB_INT_ENABLE); | |
224 | wmb(); | |
225 | ||
226 | r = __raw_readl(base + USB_DWC_CTRL1); | |
227 | r &= ~USB_DWC_CTRL1_DCRS; | |
228 | __raw_writel(r, base + USB_DWC_CTRL1); | |
229 | wmb(); | |
230 | ||
231 | __au1300_usb_phyctl(base, enable); | |
232 | } | |
233 | } | |
234 | ||
235 | static inline void __au1300_otg_control(void __iomem *base, int enable) | |
236 | { | |
237 | unsigned long r; | |
238 | if (enable) { | |
239 | r = __raw_readl(base + USB_DWC_CTRL3); | |
240 | r |= USB_DWC_CTRL3_OTG0_CKEN; | |
241 | __raw_writel(r, base + USB_DWC_CTRL3); | |
242 | wmb(); | |
243 | ||
244 | r = __raw_readl(base + USB_DWC_CTRL1); | |
245 | r &= ~USB_DWC_CTRL1_OTGD; | |
246 | __raw_writel(r, base + USB_DWC_CTRL1); | |
247 | wmb(); | |
248 | ||
249 | __au1300_usb_phyctl(base, enable); | |
250 | } else { | |
251 | r = __raw_readl(base + USB_DWC_CTRL1); | |
252 | r |= USB_DWC_CTRL1_OTGD; | |
253 | __raw_writel(r, base + USB_DWC_CTRL1); | |
254 | wmb(); | |
255 | ||
256 | r = __raw_readl(base + USB_DWC_CTRL3); | |
257 | r &= ~USB_DWC_CTRL3_OTG0_CKEN; | |
258 | __raw_writel(r, base + USB_DWC_CTRL3); | |
259 | wmb(); | |
260 | ||
261 | __au1300_usb_phyctl(base, enable); | |
262 | } | |
263 | } | |
264 | ||
265 | static inline int au1300_usb_control(int block, int enable) | |
266 | { | |
267 | void __iomem *base = | |
268 | (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); | |
269 | int ret = 0; | |
270 | ||
271 | switch (block) { | |
272 | case ALCHEMY_USB_OHCI0: | |
273 | __au1300_ohci_control(base, enable, 0); | |
274 | break; | |
275 | case ALCHEMY_USB_OHCI1: | |
276 | __au1300_ohci_control(base, enable, 1); | |
277 | break; | |
278 | case ALCHEMY_USB_EHCI0: | |
279 | __au1300_ehci_control(base, enable); | |
280 | break; | |
281 | case ALCHEMY_USB_UDC0: | |
282 | __au1300_udc_control(base, enable); | |
283 | break; | |
284 | case ALCHEMY_USB_OTG0: | |
285 | __au1300_otg_control(base, enable); | |
286 | break; | |
287 | default: | |
288 | ret = -ENODEV; | |
289 | } | |
290 | return ret; | |
291 | } | |
292 | ||
293 | static inline void au1300_usb_init(void) | |
294 | { | |
295 | void __iomem *base = | |
296 | (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); | |
297 | ||
298 | /* set some sane defaults. Note: we don't fiddle with DWC_CTRL4 | |
299 | * here at all: Port 2 routing (EHCI or UDC) must be set either | |
300 | * by boot firmware or platform init code; I can't autodetect | |
301 | * a sane setting. | |
302 | */ | |
303 | __raw_writel(0, base + USB_INT_ENABLE); /* disable all USB irqs */ | |
304 | wmb(); | |
305 | __raw_writel(0, base + USB_DWC_CTRL3); /* disable all clocks */ | |
306 | wmb(); | |
307 | __raw_writel(~0, base + USB_MSR_ERR); /* clear all errors */ | |
308 | wmb(); | |
309 | __raw_writel(~0, base + USB_INT_STATUS); /* clear int status */ | |
310 | wmb(); | |
311 | /* set coherent access bit */ | |
312 | __raw_writel(USB_SBUS_CTRL_SBCA, base + USB_SBUS_CTRL); | |
313 | wmb(); | |
314 | } | |
ce6bc922 ML |
315 | |
316 | static inline void __au1200_ohci_control(void __iomem *base, int enable) | |
317 | { | |
318 | unsigned long r = __raw_readl(base + AU1200_USBCFG); | |
319 | if (enable) { | |
320 | __raw_writel(r | USBCFG_OCE, base + AU1200_USBCFG); | |
321 | wmb(); | |
322 | udelay(2000); | |
323 | } else { | |
324 | __raw_writel(r & ~USBCFG_OCE, base + AU1200_USBCFG); | |
325 | wmb(); | |
326 | udelay(1000); | |
327 | } | |
328 | } | |
329 | ||
330 | static inline void __au1200_ehci_control(void __iomem *base, int enable) | |
331 | { | |
332 | unsigned long r = __raw_readl(base + AU1200_USBCFG); | |
333 | if (enable) { | |
334 | __raw_writel(r | USBCFG_ECE | USBCFG_PPE, base + AU1200_USBCFG); | |
335 | wmb(); | |
336 | udelay(1000); | |
337 | } else { | |
338 | if (!(r & USBCFG_UCE)) /* UDC also off? */ | |
339 | r &= ~USBCFG_PPE; /* yes: disable HS PHY PLL */ | |
340 | __raw_writel(r & ~USBCFG_ECE, base + AU1200_USBCFG); | |
341 | wmb(); | |
342 | udelay(1000); | |
343 | } | |
344 | } | |
345 | ||
346 | static inline void __au1200_udc_control(void __iomem *base, int enable) | |
347 | { | |
348 | unsigned long r = __raw_readl(base + AU1200_USBCFG); | |
349 | if (enable) { | |
350 | __raw_writel(r | USBCFG_UCE | USBCFG_PPE, base + AU1200_USBCFG); | |
351 | wmb(); | |
352 | } else { | |
353 | if (!(r & USBCFG_ECE)) /* EHCI also off? */ | |
354 | r &= ~USBCFG_PPE; /* yes: disable HS PHY PLL */ | |
355 | __raw_writel(r & ~USBCFG_UCE, base + AU1200_USBCFG); | |
356 | wmb(); | |
357 | } | |
358 | } | |
359 | ||
ce6bc922 ML |
360 | static inline int au1200_usb_control(int block, int enable) |
361 | { | |
362 | void __iomem *base = | |
363 | (void __iomem *)KSEG1ADDR(AU1200_USB_CTL_PHYS_ADDR); | |
ce6bc922 ML |
364 | |
365 | switch (block) { | |
366 | case ALCHEMY_USB_OHCI0: | |
ce6bc922 ML |
367 | __au1200_ohci_control(base, enable); |
368 | break; | |
369 | case ALCHEMY_USB_UDC0: | |
370 | __au1200_udc_control(base, enable); | |
371 | break; | |
372 | case ALCHEMY_USB_EHCI0: | |
ce6bc922 ML |
373 | __au1200_ehci_control(base, enable); |
374 | break; | |
375 | default: | |
739cec8f | 376 | return -ENODEV; |
ce6bc922 | 377 | } |
739cec8f | 378 | return 0; |
ce6bc922 ML |
379 | } |
380 | ||
381 | ||
382 | /* initialize USB block(s) to a known working state */ | |
383 | static inline void au1200_usb_init(void) | |
384 | { | |
385 | void __iomem *base = | |
386 | (void __iomem *)KSEG1ADDR(AU1200_USB_CTL_PHYS_ADDR); | |
387 | __raw_writel(USBCFG_INIT_AU1200, base + AU1200_USBCFG); | |
388 | wmb(); | |
389 | udelay(1000); | |
390 | } | |
391 | ||
3feae784 | 392 | static inline int au1000_usb_init(unsigned long rb, int reg) |
ce6bc922 ML |
393 | { |
394 | void __iomem *base = (void __iomem *)KSEG1ADDR(rb + reg); | |
395 | unsigned long r = __raw_readl(base); | |
3feae784 ML |
396 | struct clk *c; |
397 | ||
398 | /* 48MHz check. Don't init if no one can provide it */ | |
399 | c = clk_get(NULL, "usbh_clk"); | |
400 | if (IS_ERR(c)) | |
401 | return -ENODEV; | |
402 | if (clk_round_rate(c, 48000000) != 48000000) { | |
403 | clk_put(c); | |
404 | return -ENODEV; | |
405 | } | |
406 | if (clk_set_rate(c, 48000000)) { | |
407 | clk_put(c); | |
408 | return -ENODEV; | |
409 | } | |
410 | clk_put(c); | |
ce6bc922 ML |
411 | |
412 | #if defined(__BIG_ENDIAN) | |
413 | r |= USBHEN_BE; | |
414 | #endif | |
415 | r |= USBHEN_C; | |
416 | ||
417 | __raw_writel(r, base); | |
418 | wmb(); | |
419 | udelay(1000); | |
3feae784 ML |
420 | |
421 | return 0; | |
ce6bc922 ML |
422 | } |
423 | ||
424 | ||
425 | static inline void __au1xx0_ohci_control(int enable, unsigned long rb, int creg) | |
426 | { | |
427 | void __iomem *base = (void __iomem *)KSEG1ADDR(rb); | |
428 | unsigned long r = __raw_readl(base + creg); | |
3feae784 ML |
429 | struct clk *c = clk_get(NULL, "usbh_clk"); |
430 | ||
431 | if (IS_ERR(c)) | |
432 | return; | |
ce6bc922 ML |
433 | |
434 | if (enable) { | |
3feae784 ML |
435 | if (clk_prepare_enable(c)) |
436 | goto out; | |
437 | ||
ce6bc922 ML |
438 | __raw_writel(r | USBHEN_CE, base + creg); |
439 | wmb(); | |
440 | udelay(1000); | |
441 | __raw_writel(r | USBHEN_CE | USBHEN_E, base + creg); | |
442 | wmb(); | |
443 | udelay(1000); | |
444 | ||
445 | /* wait for reset complete (read reg twice: au1500 erratum) */ | |
446 | while (__raw_readl(base + creg), | |
447 | !(__raw_readl(base + creg) & USBHEN_RD)) | |
448 | udelay(1000); | |
449 | } else { | |
450 | __raw_writel(r & ~(USBHEN_CE | USBHEN_E), base + creg); | |
451 | wmb(); | |
3feae784 | 452 | clk_disable_unprepare(c); |
ce6bc922 | 453 | } |
3feae784 ML |
454 | out: |
455 | clk_put(c); | |
ce6bc922 ML |
456 | } |
457 | ||
458 | static inline int au1000_usb_control(int block, int enable, unsigned long rb, | |
459 | int creg) | |
460 | { | |
461 | int ret = 0; | |
462 | ||
463 | switch (block) { | |
464 | case ALCHEMY_USB_OHCI0: | |
465 | __au1xx0_ohci_control(enable, rb, creg); | |
466 | break; | |
467 | default: | |
468 | ret = -ENODEV; | |
469 | } | |
470 | return ret; | |
471 | } | |
472 | ||
473 | /* | |
474 | * alchemy_usb_control - control Alchemy on-chip USB blocks | |
475 | * @block: USB block to target | |
476 | * @enable: set 1 to enable a block, 0 to disable | |
477 | */ | |
478 | int alchemy_usb_control(int block, int enable) | |
479 | { | |
480 | unsigned long flags; | |
481 | int ret; | |
482 | ||
483 | spin_lock_irqsave(&alchemy_usb_lock, flags); | |
484 | switch (alchemy_get_cputype()) { | |
485 | case ALCHEMY_CPU_AU1000: | |
486 | case ALCHEMY_CPU_AU1500: | |
487 | case ALCHEMY_CPU_AU1100: | |
488 | ret = au1000_usb_control(block, enable, | |
3feae784 | 489 | AU1000_USB_OHCI_PHYS_ADDR, AU1000_OHCICFG); |
ce6bc922 ML |
490 | break; |
491 | case ALCHEMY_CPU_AU1550: | |
492 | ret = au1000_usb_control(block, enable, | |
3feae784 | 493 | AU1550_USB_OHCI_PHYS_ADDR, AU1550_OHCICFG); |
ce6bc922 ML |
494 | break; |
495 | case ALCHEMY_CPU_AU1200: | |
496 | ret = au1200_usb_control(block, enable); | |
497 | break; | |
809f36c6 ML |
498 | case ALCHEMY_CPU_AU1300: |
499 | ret = au1300_usb_control(block, enable); | |
500 | break; | |
ce6bc922 ML |
501 | default: |
502 | ret = -ENODEV; | |
503 | } | |
504 | spin_unlock_irqrestore(&alchemy_usb_lock, flags); | |
505 | return ret; | |
506 | } | |
507 | EXPORT_SYMBOL_GPL(alchemy_usb_control); | |
508 | ||
509 | ||
510 | static unsigned long alchemy_usb_pmdata[2]; | |
511 | ||
512 | static void au1000_usb_pm(unsigned long br, int creg, int susp) | |
513 | { | |
514 | void __iomem *base = (void __iomem *)KSEG1ADDR(br); | |
515 | ||
516 | if (susp) { | |
517 | alchemy_usb_pmdata[0] = __raw_readl(base + creg); | |
518 | /* There appears to be some undocumented reset register.... */ | |
519 | __raw_writel(0, base + 0x04); | |
520 | wmb(); | |
521 | __raw_writel(0, base + creg); | |
522 | wmb(); | |
523 | } else { | |
524 | __raw_writel(alchemy_usb_pmdata[0], base + creg); | |
525 | wmb(); | |
526 | } | |
527 | } | |
528 | ||
529 | static void au1200_usb_pm(int susp) | |
530 | { | |
531 | void __iomem *base = | |
532 | (void __iomem *)KSEG1ADDR(AU1200_USB_OTG_PHYS_ADDR); | |
533 | if (susp) { | |
534 | /* save OTG_CAP/MUX registers which indicate port routing */ | |
535 | /* FIXME: write an OTG driver to do that */ | |
536 | alchemy_usb_pmdata[0] = __raw_readl(base + 0x00); | |
537 | alchemy_usb_pmdata[1] = __raw_readl(base + 0x04); | |
538 | } else { | |
539 | /* restore access to all MMIO areas */ | |
540 | au1200_usb_init(); | |
541 | ||
542 | /* restore OTG_CAP/MUX registers */ | |
543 | __raw_writel(alchemy_usb_pmdata[0], base + 0x00); | |
544 | __raw_writel(alchemy_usb_pmdata[1], base + 0x04); | |
545 | wmb(); | |
546 | } | |
547 | } | |
548 | ||
809f36c6 ML |
549 | static void au1300_usb_pm(int susp) |
550 | { | |
551 | void __iomem *base = | |
552 | (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); | |
553 | /* remember Port2 routing */ | |
554 | if (susp) { | |
555 | alchemy_usb_pmdata[0] = __raw_readl(base + USB_DWC_CTRL4); | |
556 | } else { | |
557 | au1300_usb_init(); | |
558 | __raw_writel(alchemy_usb_pmdata[0], base + USB_DWC_CTRL4); | |
559 | wmb(); | |
560 | } | |
561 | } | |
562 | ||
ce6bc922 ML |
563 | static void alchemy_usb_pm(int susp) |
564 | { | |
565 | switch (alchemy_get_cputype()) { | |
566 | case ALCHEMY_CPU_AU1000: | |
567 | case ALCHEMY_CPU_AU1500: | |
568 | case ALCHEMY_CPU_AU1100: | |
569 | au1000_usb_pm(AU1000_USB_OHCI_PHYS_ADDR, AU1000_OHCICFG, susp); | |
570 | break; | |
571 | case ALCHEMY_CPU_AU1550: | |
572 | au1000_usb_pm(AU1550_USB_OHCI_PHYS_ADDR, AU1550_OHCICFG, susp); | |
573 | break; | |
574 | case ALCHEMY_CPU_AU1200: | |
575 | au1200_usb_pm(susp); | |
576 | break; | |
809f36c6 ML |
577 | case ALCHEMY_CPU_AU1300: |
578 | au1300_usb_pm(susp); | |
579 | break; | |
ce6bc922 ML |
580 | } |
581 | } | |
582 | ||
583 | static int alchemy_usb_suspend(void) | |
584 | { | |
585 | alchemy_usb_pm(1); | |
586 | return 0; | |
587 | } | |
588 | ||
589 | static void alchemy_usb_resume(void) | |
590 | { | |
591 | alchemy_usb_pm(0); | |
592 | } | |
593 | ||
594 | static struct syscore_ops alchemy_usb_pm_ops = { | |
595 | .suspend = alchemy_usb_suspend, | |
596 | .resume = alchemy_usb_resume, | |
597 | }; | |
598 | ||
599 | static int __init alchemy_usb_init(void) | |
600 | { | |
3feae784 ML |
601 | int ret = 0; |
602 | ||
ce6bc922 ML |
603 | switch (alchemy_get_cputype()) { |
604 | case ALCHEMY_CPU_AU1000: | |
605 | case ALCHEMY_CPU_AU1500: | |
606 | case ALCHEMY_CPU_AU1100: | |
3feae784 ML |
607 | ret = au1000_usb_init(AU1000_USB_OHCI_PHYS_ADDR, |
608 | AU1000_OHCICFG); | |
ce6bc922 ML |
609 | break; |
610 | case ALCHEMY_CPU_AU1550: | |
3feae784 ML |
611 | ret = au1000_usb_init(AU1550_USB_OHCI_PHYS_ADDR, |
612 | AU1550_OHCICFG); | |
ce6bc922 ML |
613 | break; |
614 | case ALCHEMY_CPU_AU1200: | |
615 | au1200_usb_init(); | |
616 | break; | |
809f36c6 ML |
617 | case ALCHEMY_CPU_AU1300: |
618 | au1300_usb_init(); | |
619 | break; | |
ce6bc922 ML |
620 | } |
621 | ||
3feae784 ML |
622 | if (!ret) |
623 | register_syscore_ops(&alchemy_usb_pm_ops); | |
ce6bc922 | 624 | |
3feae784 | 625 | return ret; |
ce6bc922 ML |
626 | } |
627 | arch_initcall(alchemy_usb_init); |