1*457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2216d0fdeSFlorian Fainelli /*
3216d0fdeSFlorian Fainelli * USB block power/access management abstraction.
4216d0fdeSFlorian Fainelli *
5216d0fdeSFlorian Fainelli * Au1000+: The OHCI block control register is at the far end of the OHCI memory
6216d0fdeSFlorian Fainelli * area. Au1550 has OHCI on different base address. No need to handle
7216d0fdeSFlorian Fainelli * UDC here.
8216d0fdeSFlorian Fainelli * Au1200: one register to control access and clocks to O/EHCI, UDC and OTG
9216d0fdeSFlorian Fainelli * as well as the PHY for EHCI and UDC.
10216d0fdeSFlorian Fainelli *
11216d0fdeSFlorian Fainelli */
12216d0fdeSFlorian Fainelli
133feae784SManuel Lauss #include <linux/clk.h>
1426dd3e4fSPaul Gortmaker #include <linux/export.h>
15216d0fdeSFlorian Fainelli #include <linux/init.h>
16216d0fdeSFlorian Fainelli #include <linux/io.h>
17216d0fdeSFlorian Fainelli #include <linux/spinlock.h>
18216d0fdeSFlorian Fainelli #include <linux/syscore_ops.h>
198ff374b9SMaciej W. Rozycki #include <asm/cpu.h>
20216d0fdeSFlorian Fainelli #include <asm/mach-au1x00/au1000.h>
21216d0fdeSFlorian Fainelli
22216d0fdeSFlorian Fainelli /* control register offsets */
23216d0fdeSFlorian Fainelli #define AU1000_OHCICFG 0x7fffc
24216d0fdeSFlorian Fainelli #define AU1550_OHCICFG 0x07ffc
25216d0fdeSFlorian Fainelli #define AU1200_USBCFG 0x04
26216d0fdeSFlorian Fainelli
27216d0fdeSFlorian Fainelli /* Au1000 USB block config bits */
28216d0fdeSFlorian Fainelli #define USBHEN_RD (1 << 4) /* OHCI reset-done indicator */
29216d0fdeSFlorian Fainelli #define USBHEN_CE (1 << 3) /* OHCI block clock enable */
30216d0fdeSFlorian Fainelli #define USBHEN_E (1 << 2) /* OHCI block enable */
31216d0fdeSFlorian Fainelli #define USBHEN_C (1 << 1) /* OHCI block coherency bit */
32216d0fdeSFlorian Fainelli #define USBHEN_BE (1 << 0) /* OHCI Big-Endian */
33216d0fdeSFlorian Fainelli
34216d0fdeSFlorian Fainelli /* Au1200 USB config bits */
35216d0fdeSFlorian Fainelli #define USBCFG_PFEN (1 << 31) /* prefetch enable (undoc) */
36216d0fdeSFlorian Fainelli #define USBCFG_RDCOMB (1 << 30) /* read combining (undoc) */
37216d0fdeSFlorian Fainelli #define USBCFG_UNKNOWN (5 << 20) /* unknown, leave this way */
38216d0fdeSFlorian Fainelli #define USBCFG_SSD (1 << 23) /* serial short detect en */
39216d0fdeSFlorian Fainelli #define USBCFG_PPE (1 << 19) /* HS PHY PLL */
40216d0fdeSFlorian Fainelli #define USBCFG_UCE (1 << 18) /* UDC clock enable */
41216d0fdeSFlorian Fainelli #define USBCFG_ECE (1 << 17) /* EHCI clock enable */
42216d0fdeSFlorian Fainelli #define USBCFG_OCE (1 << 16) /* OHCI clock enable */
43216d0fdeSFlorian Fainelli #define USBCFG_FLA(x) (((x) & 0x3f) << 8)
44216d0fdeSFlorian Fainelli #define USBCFG_UCAM (1 << 7) /* coherent access (undoc) */
45216d0fdeSFlorian Fainelli #define USBCFG_GME (1 << 6) /* OTG mem access */
46216d0fdeSFlorian Fainelli #define USBCFG_DBE (1 << 5) /* UDC busmaster enable */
47216d0fdeSFlorian Fainelli #define USBCFG_DME (1 << 4) /* UDC mem enable */
48216d0fdeSFlorian Fainelli #define USBCFG_EBE (1 << 3) /* EHCI busmaster enable */
49216d0fdeSFlorian Fainelli #define USBCFG_EME (1 << 2) /* EHCI mem enable */
50216d0fdeSFlorian Fainelli #define USBCFG_OBE (1 << 1) /* OHCI busmaster enable */
51216d0fdeSFlorian Fainelli #define USBCFG_OME (1 << 0) /* OHCI mem enable */
52216d0fdeSFlorian Fainelli #define USBCFG_INIT_AU1200 (USBCFG_PFEN | USBCFG_RDCOMB | USBCFG_UNKNOWN |\
53216d0fdeSFlorian Fainelli USBCFG_SSD | USBCFG_FLA(0x20) | USBCFG_UCAM | \
54216d0fdeSFlorian Fainelli USBCFG_GME | USBCFG_DBE | USBCFG_DME | \
55216d0fdeSFlorian Fainelli USBCFG_EBE | USBCFG_EME | USBCFG_OBE | \
56216d0fdeSFlorian Fainelli USBCFG_OME)
57216d0fdeSFlorian Fainelli
58216d0fdeSFlorian Fainelli /* Au1300 USB config registers */
59216d0fdeSFlorian Fainelli #define USB_DWC_CTRL1 0x00
60216d0fdeSFlorian Fainelli #define USB_DWC_CTRL2 0x04
61216d0fdeSFlorian Fainelli #define USB_VBUS_TIMER 0x10
62216d0fdeSFlorian Fainelli #define USB_SBUS_CTRL 0x14
63216d0fdeSFlorian Fainelli #define USB_MSR_ERR 0x18
64216d0fdeSFlorian Fainelli #define USB_DWC_CTRL3 0x1C
65216d0fdeSFlorian Fainelli #define USB_DWC_CTRL4 0x20
66216d0fdeSFlorian Fainelli #define USB_OTG_STATUS 0x28
67216d0fdeSFlorian Fainelli #define USB_DWC_CTRL5 0x2C
68216d0fdeSFlorian Fainelli #define USB_DWC_CTRL6 0x30
69216d0fdeSFlorian Fainelli #define USB_DWC_CTRL7 0x34
70216d0fdeSFlorian Fainelli #define USB_PHY_STATUS 0xC0
71216d0fdeSFlorian Fainelli #define USB_INT_STATUS 0xC4
72216d0fdeSFlorian Fainelli #define USB_INT_ENABLE 0xC8
73216d0fdeSFlorian Fainelli
74216d0fdeSFlorian Fainelli #define USB_DWC_CTRL1_OTGD 0x04 /* set to DISable OTG */
75216d0fdeSFlorian Fainelli #define USB_DWC_CTRL1_HSTRS 0x02 /* set to ENable EHCI */
76216d0fdeSFlorian Fainelli #define USB_DWC_CTRL1_DCRS 0x01 /* set to ENable UDC */
77216d0fdeSFlorian Fainelli
78216d0fdeSFlorian Fainelli #define USB_DWC_CTRL2_PHY1RS 0x04 /* set to enable PHY1 */
79216d0fdeSFlorian Fainelli #define USB_DWC_CTRL2_PHY0RS 0x02 /* set to enable PHY0 */
80216d0fdeSFlorian Fainelli #define USB_DWC_CTRL2_PHYRS 0x01 /* set to enable PHY */
81216d0fdeSFlorian Fainelli
82216d0fdeSFlorian Fainelli #define USB_DWC_CTRL3_OHCI1_CKEN (1 << 19)
83216d0fdeSFlorian Fainelli #define USB_DWC_CTRL3_OHCI0_CKEN (1 << 18)
84216d0fdeSFlorian Fainelli #define USB_DWC_CTRL3_EHCI0_CKEN (1 << 17)
85216d0fdeSFlorian Fainelli #define USB_DWC_CTRL3_OTG0_CKEN (1 << 16)
86216d0fdeSFlorian Fainelli
87216d0fdeSFlorian Fainelli #define USB_SBUS_CTRL_SBCA 0x04 /* coherent access */
88216d0fdeSFlorian Fainelli
89216d0fdeSFlorian Fainelli #define USB_INTEN_FORCE 0x20
90216d0fdeSFlorian Fainelli #define USB_INTEN_PHY 0x10
91216d0fdeSFlorian Fainelli #define USB_INTEN_UDC 0x08
92216d0fdeSFlorian Fainelli #define USB_INTEN_EHCI 0x04
93216d0fdeSFlorian Fainelli #define USB_INTEN_OHCI1 0x02
94216d0fdeSFlorian Fainelli #define USB_INTEN_OHCI0 0x01
95216d0fdeSFlorian Fainelli
96216d0fdeSFlorian Fainelli static DEFINE_SPINLOCK(alchemy_usb_lock);
97216d0fdeSFlorian Fainelli
__au1300_usb_phyctl(void __iomem * base,int enable)98216d0fdeSFlorian Fainelli static inline void __au1300_usb_phyctl(void __iomem *base, int enable)
99216d0fdeSFlorian Fainelli {
100216d0fdeSFlorian Fainelli unsigned long r, s;
101216d0fdeSFlorian Fainelli
102216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_DWC_CTRL2);
103216d0fdeSFlorian Fainelli s = __raw_readl(base + USB_DWC_CTRL3);
104216d0fdeSFlorian Fainelli
105216d0fdeSFlorian Fainelli s &= USB_DWC_CTRL3_OHCI1_CKEN | USB_DWC_CTRL3_OHCI0_CKEN |
106216d0fdeSFlorian Fainelli USB_DWC_CTRL3_EHCI0_CKEN | USB_DWC_CTRL3_OTG0_CKEN;
107216d0fdeSFlorian Fainelli
108216d0fdeSFlorian Fainelli if (enable) {
109216d0fdeSFlorian Fainelli /* simply enable all PHYs */
110216d0fdeSFlorian Fainelli r |= USB_DWC_CTRL2_PHY1RS | USB_DWC_CTRL2_PHY0RS |
111216d0fdeSFlorian Fainelli USB_DWC_CTRL2_PHYRS;
112216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_DWC_CTRL2);
113216d0fdeSFlorian Fainelli wmb();
114216d0fdeSFlorian Fainelli } else if (!s) {
115216d0fdeSFlorian Fainelli /* no USB block active, do disable all PHYs */
116216d0fdeSFlorian Fainelli r &= ~(USB_DWC_CTRL2_PHY1RS | USB_DWC_CTRL2_PHY0RS |
117216d0fdeSFlorian Fainelli USB_DWC_CTRL2_PHYRS);
118216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_DWC_CTRL2);
119216d0fdeSFlorian Fainelli wmb();
120216d0fdeSFlorian Fainelli }
121216d0fdeSFlorian Fainelli }
122216d0fdeSFlorian Fainelli
__au1300_ohci_control(void __iomem * base,int enable,int id)123216d0fdeSFlorian Fainelli static inline void __au1300_ohci_control(void __iomem *base, int enable, int id)
124216d0fdeSFlorian Fainelli {
125216d0fdeSFlorian Fainelli unsigned long r;
126216d0fdeSFlorian Fainelli
127216d0fdeSFlorian Fainelli if (enable) {
128216d0fdeSFlorian Fainelli __raw_writel(1, base + USB_DWC_CTRL7); /* start OHCI clock */
129216d0fdeSFlorian Fainelli wmb();
130216d0fdeSFlorian Fainelli
131216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_DWC_CTRL3); /* enable OHCI block */
132216d0fdeSFlorian Fainelli r |= (id == 0) ? USB_DWC_CTRL3_OHCI0_CKEN
133216d0fdeSFlorian Fainelli : USB_DWC_CTRL3_OHCI1_CKEN;
134216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_DWC_CTRL3);
135216d0fdeSFlorian Fainelli wmb();
136216d0fdeSFlorian Fainelli
137216d0fdeSFlorian Fainelli __au1300_usb_phyctl(base, enable); /* power up the PHYs */
138216d0fdeSFlorian Fainelli
139216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_INT_ENABLE);
140216d0fdeSFlorian Fainelli r |= (id == 0) ? USB_INTEN_OHCI0 : USB_INTEN_OHCI1;
141216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_INT_ENABLE);
142216d0fdeSFlorian Fainelli wmb();
143216d0fdeSFlorian Fainelli
144216d0fdeSFlorian Fainelli /* reset the OHCI start clock bit */
145216d0fdeSFlorian Fainelli __raw_writel(0, base + USB_DWC_CTRL7);
146216d0fdeSFlorian Fainelli wmb();
147216d0fdeSFlorian Fainelli } else {
148216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_INT_ENABLE);
149216d0fdeSFlorian Fainelli r &= ~((id == 0) ? USB_INTEN_OHCI0 : USB_INTEN_OHCI1);
150216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_INT_ENABLE);
151216d0fdeSFlorian Fainelli wmb();
152216d0fdeSFlorian Fainelli
153216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_DWC_CTRL3);
154216d0fdeSFlorian Fainelli r &= ~((id == 0) ? USB_DWC_CTRL3_OHCI0_CKEN
155216d0fdeSFlorian Fainelli : USB_DWC_CTRL3_OHCI1_CKEN);
156216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_DWC_CTRL3);
157216d0fdeSFlorian Fainelli wmb();
158216d0fdeSFlorian Fainelli
159216d0fdeSFlorian Fainelli __au1300_usb_phyctl(base, enable);
160216d0fdeSFlorian Fainelli }
161216d0fdeSFlorian Fainelli }
162216d0fdeSFlorian Fainelli
__au1300_ehci_control(void __iomem * base,int enable)163216d0fdeSFlorian Fainelli static inline void __au1300_ehci_control(void __iomem *base, int enable)
164216d0fdeSFlorian Fainelli {
165216d0fdeSFlorian Fainelli unsigned long r;
166216d0fdeSFlorian Fainelli
167216d0fdeSFlorian Fainelli if (enable) {
168216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_DWC_CTRL3);
169216d0fdeSFlorian Fainelli r |= USB_DWC_CTRL3_EHCI0_CKEN;
170216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_DWC_CTRL3);
171216d0fdeSFlorian Fainelli wmb();
172216d0fdeSFlorian Fainelli
173216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_DWC_CTRL1);
174216d0fdeSFlorian Fainelli r |= USB_DWC_CTRL1_HSTRS;
175216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_DWC_CTRL1);
176216d0fdeSFlorian Fainelli wmb();
177216d0fdeSFlorian Fainelli
178216d0fdeSFlorian Fainelli __au1300_usb_phyctl(base, enable);
179216d0fdeSFlorian Fainelli
180216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_INT_ENABLE);
181216d0fdeSFlorian Fainelli r |= USB_INTEN_EHCI;
182216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_INT_ENABLE);
183216d0fdeSFlorian Fainelli wmb();
184216d0fdeSFlorian Fainelli } else {
185216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_INT_ENABLE);
186216d0fdeSFlorian Fainelli r &= ~USB_INTEN_EHCI;
187216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_INT_ENABLE);
188216d0fdeSFlorian Fainelli wmb();
189216d0fdeSFlorian Fainelli
190216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_DWC_CTRL1);
191216d0fdeSFlorian Fainelli r &= ~USB_DWC_CTRL1_HSTRS;
192216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_DWC_CTRL1);
193216d0fdeSFlorian Fainelli wmb();
194216d0fdeSFlorian Fainelli
195216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_DWC_CTRL3);
196216d0fdeSFlorian Fainelli r &= ~USB_DWC_CTRL3_EHCI0_CKEN;
197216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_DWC_CTRL3);
198216d0fdeSFlorian Fainelli wmb();
199216d0fdeSFlorian Fainelli
200216d0fdeSFlorian Fainelli __au1300_usb_phyctl(base, enable);
201216d0fdeSFlorian Fainelli }
202216d0fdeSFlorian Fainelli }
203216d0fdeSFlorian Fainelli
__au1300_udc_control(void __iomem * base,int enable)204216d0fdeSFlorian Fainelli static inline void __au1300_udc_control(void __iomem *base, int enable)
205216d0fdeSFlorian Fainelli {
206216d0fdeSFlorian Fainelli unsigned long r;
207216d0fdeSFlorian Fainelli
208216d0fdeSFlorian Fainelli if (enable) {
209216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_DWC_CTRL1);
210216d0fdeSFlorian Fainelli r |= USB_DWC_CTRL1_DCRS;
211216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_DWC_CTRL1);
212216d0fdeSFlorian Fainelli wmb();
213216d0fdeSFlorian Fainelli
214216d0fdeSFlorian Fainelli __au1300_usb_phyctl(base, enable);
215216d0fdeSFlorian Fainelli
216216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_INT_ENABLE);
217216d0fdeSFlorian Fainelli r |= USB_INTEN_UDC;
218216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_INT_ENABLE);
219216d0fdeSFlorian Fainelli wmb();
220216d0fdeSFlorian Fainelli } else {
221216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_INT_ENABLE);
222216d0fdeSFlorian Fainelli r &= ~USB_INTEN_UDC;
223216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_INT_ENABLE);
224216d0fdeSFlorian Fainelli wmb();
225216d0fdeSFlorian Fainelli
226216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_DWC_CTRL1);
227216d0fdeSFlorian Fainelli r &= ~USB_DWC_CTRL1_DCRS;
228216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_DWC_CTRL1);
229216d0fdeSFlorian Fainelli wmb();
230216d0fdeSFlorian Fainelli
231216d0fdeSFlorian Fainelli __au1300_usb_phyctl(base, enable);
232216d0fdeSFlorian Fainelli }
233216d0fdeSFlorian Fainelli }
234216d0fdeSFlorian Fainelli
__au1300_otg_control(void __iomem * base,int enable)235216d0fdeSFlorian Fainelli static inline void __au1300_otg_control(void __iomem *base, int enable)
236216d0fdeSFlorian Fainelli {
237216d0fdeSFlorian Fainelli unsigned long r;
238216d0fdeSFlorian Fainelli if (enable) {
239216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_DWC_CTRL3);
240216d0fdeSFlorian Fainelli r |= USB_DWC_CTRL3_OTG0_CKEN;
241216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_DWC_CTRL3);
242216d0fdeSFlorian Fainelli wmb();
243216d0fdeSFlorian Fainelli
244216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_DWC_CTRL1);
245216d0fdeSFlorian Fainelli r &= ~USB_DWC_CTRL1_OTGD;
246216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_DWC_CTRL1);
247216d0fdeSFlorian Fainelli wmb();
248216d0fdeSFlorian Fainelli
249216d0fdeSFlorian Fainelli __au1300_usb_phyctl(base, enable);
250216d0fdeSFlorian Fainelli } else {
251216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_DWC_CTRL1);
252216d0fdeSFlorian Fainelli r |= USB_DWC_CTRL1_OTGD;
253216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_DWC_CTRL1);
254216d0fdeSFlorian Fainelli wmb();
255216d0fdeSFlorian Fainelli
256216d0fdeSFlorian Fainelli r = __raw_readl(base + USB_DWC_CTRL3);
257216d0fdeSFlorian Fainelli r &= ~USB_DWC_CTRL3_OTG0_CKEN;
258216d0fdeSFlorian Fainelli __raw_writel(r, base + USB_DWC_CTRL3);
259216d0fdeSFlorian Fainelli wmb();
260216d0fdeSFlorian Fainelli
261216d0fdeSFlorian Fainelli __au1300_usb_phyctl(base, enable);
262216d0fdeSFlorian Fainelli }
263216d0fdeSFlorian Fainelli }
264216d0fdeSFlorian Fainelli
au1300_usb_control(int block,int enable)265216d0fdeSFlorian Fainelli static inline int au1300_usb_control(int block, int enable)
266216d0fdeSFlorian Fainelli {
267216d0fdeSFlorian Fainelli void __iomem *base =
268216d0fdeSFlorian Fainelli (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR);
269216d0fdeSFlorian Fainelli int ret = 0;
270216d0fdeSFlorian Fainelli
271216d0fdeSFlorian Fainelli switch (block) {
272216d0fdeSFlorian Fainelli case ALCHEMY_USB_OHCI0:
273216d0fdeSFlorian Fainelli __au1300_ohci_control(base, enable, 0);
274216d0fdeSFlorian Fainelli break;
275216d0fdeSFlorian Fainelli case ALCHEMY_USB_OHCI1:
276216d0fdeSFlorian Fainelli __au1300_ohci_control(base, enable, 1);
277216d0fdeSFlorian Fainelli break;
278216d0fdeSFlorian Fainelli case ALCHEMY_USB_EHCI0:
279216d0fdeSFlorian Fainelli __au1300_ehci_control(base, enable);
280216d0fdeSFlorian Fainelli break;
281216d0fdeSFlorian Fainelli case ALCHEMY_USB_UDC0:
282216d0fdeSFlorian Fainelli __au1300_udc_control(base, enable);
283216d0fdeSFlorian Fainelli break;
284216d0fdeSFlorian Fainelli case ALCHEMY_USB_OTG0:
285216d0fdeSFlorian Fainelli __au1300_otg_control(base, enable);
286216d0fdeSFlorian Fainelli break;
287216d0fdeSFlorian Fainelli default:
288216d0fdeSFlorian Fainelli ret = -ENODEV;
289216d0fdeSFlorian Fainelli }
290216d0fdeSFlorian Fainelli return ret;
291216d0fdeSFlorian Fainelli }
292216d0fdeSFlorian Fainelli
au1300_usb_init(void)293216d0fdeSFlorian Fainelli static inline void au1300_usb_init(void)
294216d0fdeSFlorian Fainelli {
295216d0fdeSFlorian Fainelli void __iomem *base =
296216d0fdeSFlorian Fainelli (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR);
297216d0fdeSFlorian Fainelli
298216d0fdeSFlorian Fainelli /* set some sane defaults. Note: we don't fiddle with DWC_CTRL4
299216d0fdeSFlorian Fainelli * here at all: Port 2 routing (EHCI or UDC) must be set either
300216d0fdeSFlorian Fainelli * by boot firmware or platform init code; I can't autodetect
301216d0fdeSFlorian Fainelli * a sane setting.
302216d0fdeSFlorian Fainelli */
303216d0fdeSFlorian Fainelli __raw_writel(0, base + USB_INT_ENABLE); /* disable all USB irqs */
304216d0fdeSFlorian Fainelli wmb();
305216d0fdeSFlorian Fainelli __raw_writel(0, base + USB_DWC_CTRL3); /* disable all clocks */
306216d0fdeSFlorian Fainelli wmb();
307216d0fdeSFlorian Fainelli __raw_writel(~0, base + USB_MSR_ERR); /* clear all errors */
308216d0fdeSFlorian Fainelli wmb();
309216d0fdeSFlorian Fainelli __raw_writel(~0, base + USB_INT_STATUS); /* clear int status */
310216d0fdeSFlorian Fainelli wmb();
311216d0fdeSFlorian Fainelli /* set coherent access bit */
312216d0fdeSFlorian Fainelli __raw_writel(USB_SBUS_CTRL_SBCA, base + USB_SBUS_CTRL);
313216d0fdeSFlorian Fainelli wmb();
314216d0fdeSFlorian Fainelli }
315216d0fdeSFlorian Fainelli
__au1200_ohci_control(void __iomem * base,int enable)316216d0fdeSFlorian Fainelli static inline void __au1200_ohci_control(void __iomem *base, int enable)
317216d0fdeSFlorian Fainelli {
318216d0fdeSFlorian Fainelli unsigned long r = __raw_readl(base + AU1200_USBCFG);
319216d0fdeSFlorian Fainelli if (enable) {
320216d0fdeSFlorian Fainelli __raw_writel(r | USBCFG_OCE, base + AU1200_USBCFG);
321216d0fdeSFlorian Fainelli wmb();
322216d0fdeSFlorian Fainelli udelay(2000);
323216d0fdeSFlorian Fainelli } else {
324216d0fdeSFlorian Fainelli __raw_writel(r & ~USBCFG_OCE, base + AU1200_USBCFG);
325216d0fdeSFlorian Fainelli wmb();
326216d0fdeSFlorian Fainelli udelay(1000);
327216d0fdeSFlorian Fainelli }
328216d0fdeSFlorian Fainelli }
329216d0fdeSFlorian Fainelli
__au1200_ehci_control(void __iomem * base,int enable)330216d0fdeSFlorian Fainelli static inline void __au1200_ehci_control(void __iomem *base, int enable)
331216d0fdeSFlorian Fainelli {
332216d0fdeSFlorian Fainelli unsigned long r = __raw_readl(base + AU1200_USBCFG);
333216d0fdeSFlorian Fainelli if (enable) {
334216d0fdeSFlorian Fainelli __raw_writel(r | USBCFG_ECE | USBCFG_PPE, base + AU1200_USBCFG);
335216d0fdeSFlorian Fainelli wmb();
336216d0fdeSFlorian Fainelli udelay(1000);
337216d0fdeSFlorian Fainelli } else {
338216d0fdeSFlorian Fainelli if (!(r & USBCFG_UCE)) /* UDC also off? */
339216d0fdeSFlorian Fainelli r &= ~USBCFG_PPE; /* yes: disable HS PHY PLL */
340216d0fdeSFlorian Fainelli __raw_writel(r & ~USBCFG_ECE, base + AU1200_USBCFG);
341216d0fdeSFlorian Fainelli wmb();
342216d0fdeSFlorian Fainelli udelay(1000);
343216d0fdeSFlorian Fainelli }
344216d0fdeSFlorian Fainelli }
345216d0fdeSFlorian Fainelli
__au1200_udc_control(void __iomem * base,int enable)346216d0fdeSFlorian Fainelli static inline void __au1200_udc_control(void __iomem *base, int enable)
347216d0fdeSFlorian Fainelli {
348216d0fdeSFlorian Fainelli unsigned long r = __raw_readl(base + AU1200_USBCFG);
349216d0fdeSFlorian Fainelli if (enable) {
350216d0fdeSFlorian Fainelli __raw_writel(r | USBCFG_UCE | USBCFG_PPE, base + AU1200_USBCFG);
351216d0fdeSFlorian Fainelli wmb();
352216d0fdeSFlorian Fainelli } else {
353216d0fdeSFlorian Fainelli if (!(r & USBCFG_ECE)) /* EHCI also off? */
354216d0fdeSFlorian Fainelli r &= ~USBCFG_PPE; /* yes: disable HS PHY PLL */
355216d0fdeSFlorian Fainelli __raw_writel(r & ~USBCFG_UCE, base + AU1200_USBCFG);
356216d0fdeSFlorian Fainelli wmb();
357216d0fdeSFlorian Fainelli }
358216d0fdeSFlorian Fainelli }
359216d0fdeSFlorian Fainelli
au1200_usb_control(int block,int enable)360216d0fdeSFlorian Fainelli static inline int au1200_usb_control(int block, int enable)
361216d0fdeSFlorian Fainelli {
362216d0fdeSFlorian Fainelli void __iomem *base =
363216d0fdeSFlorian Fainelli (void __iomem *)KSEG1ADDR(AU1200_USB_CTL_PHYS_ADDR);
364216d0fdeSFlorian Fainelli
365216d0fdeSFlorian Fainelli switch (block) {
366216d0fdeSFlorian Fainelli case ALCHEMY_USB_OHCI0:
367216d0fdeSFlorian Fainelli __au1200_ohci_control(base, enable);
368216d0fdeSFlorian Fainelli break;
369216d0fdeSFlorian Fainelli case ALCHEMY_USB_UDC0:
370216d0fdeSFlorian Fainelli __au1200_udc_control(base, enable);
371216d0fdeSFlorian Fainelli break;
372216d0fdeSFlorian Fainelli case ALCHEMY_USB_EHCI0:
373216d0fdeSFlorian Fainelli __au1200_ehci_control(base, enable);
374216d0fdeSFlorian Fainelli break;
375216d0fdeSFlorian Fainelli default:
376739cec8fSManuel Lauss return -ENODEV;
377216d0fdeSFlorian Fainelli }
378739cec8fSManuel Lauss return 0;
379216d0fdeSFlorian Fainelli }
380216d0fdeSFlorian Fainelli
381216d0fdeSFlorian Fainelli
382216d0fdeSFlorian Fainelli /* initialize USB block(s) to a known working state */
au1200_usb_init(void)383216d0fdeSFlorian Fainelli static inline void au1200_usb_init(void)
384216d0fdeSFlorian Fainelli {
385216d0fdeSFlorian Fainelli void __iomem *base =
386216d0fdeSFlorian Fainelli (void __iomem *)KSEG1ADDR(AU1200_USB_CTL_PHYS_ADDR);
387216d0fdeSFlorian Fainelli __raw_writel(USBCFG_INIT_AU1200, base + AU1200_USBCFG);
388216d0fdeSFlorian Fainelli wmb();
389216d0fdeSFlorian Fainelli udelay(1000);
390216d0fdeSFlorian Fainelli }
391216d0fdeSFlorian Fainelli
au1000_usb_init(unsigned long rb,int reg)3923feae784SManuel Lauss static inline int au1000_usb_init(unsigned long rb, int reg)
393216d0fdeSFlorian Fainelli {
394216d0fdeSFlorian Fainelli void __iomem *base = (void __iomem *)KSEG1ADDR(rb + reg);
395216d0fdeSFlorian Fainelli unsigned long r = __raw_readl(base);
3963feae784SManuel Lauss struct clk *c;
3973feae784SManuel Lauss
3983feae784SManuel Lauss /* 48MHz check. Don't init if no one can provide it */
3993feae784SManuel Lauss c = clk_get(NULL, "usbh_clk");
4003feae784SManuel Lauss if (IS_ERR(c))
4013feae784SManuel Lauss return -ENODEV;
4023feae784SManuel Lauss if (clk_round_rate(c, 48000000) != 48000000) {
4033feae784SManuel Lauss clk_put(c);
4043feae784SManuel Lauss return -ENODEV;
4053feae784SManuel Lauss }
4063feae784SManuel Lauss if (clk_set_rate(c, 48000000)) {
4073feae784SManuel Lauss clk_put(c);
4083feae784SManuel Lauss return -ENODEV;
4093feae784SManuel Lauss }
4103feae784SManuel Lauss clk_put(c);
411216d0fdeSFlorian Fainelli
412216d0fdeSFlorian Fainelli #if defined(__BIG_ENDIAN)
413216d0fdeSFlorian Fainelli r |= USBHEN_BE;
414216d0fdeSFlorian Fainelli #endif
415216d0fdeSFlorian Fainelli r |= USBHEN_C;
416216d0fdeSFlorian Fainelli
417216d0fdeSFlorian Fainelli __raw_writel(r, base);
418216d0fdeSFlorian Fainelli wmb();
419216d0fdeSFlorian Fainelli udelay(1000);
4203feae784SManuel Lauss
4213feae784SManuel Lauss return 0;
422216d0fdeSFlorian Fainelli }
423216d0fdeSFlorian Fainelli
424216d0fdeSFlorian Fainelli
__au1xx0_ohci_control(int enable,unsigned long rb,int creg)425216d0fdeSFlorian Fainelli static inline void __au1xx0_ohci_control(int enable, unsigned long rb, int creg)
426216d0fdeSFlorian Fainelli {
427216d0fdeSFlorian Fainelli void __iomem *base = (void __iomem *)KSEG1ADDR(rb);
428216d0fdeSFlorian Fainelli unsigned long r = __raw_readl(base + creg);
4293feae784SManuel Lauss struct clk *c = clk_get(NULL, "usbh_clk");
4303feae784SManuel Lauss
4313feae784SManuel Lauss if (IS_ERR(c))
4323feae784SManuel Lauss return;
433216d0fdeSFlorian Fainelli
434216d0fdeSFlorian Fainelli if (enable) {
4353feae784SManuel Lauss if (clk_prepare_enable(c))
4363feae784SManuel Lauss goto out;
4373feae784SManuel Lauss
438216d0fdeSFlorian Fainelli __raw_writel(r | USBHEN_CE, base + creg);
439216d0fdeSFlorian Fainelli wmb();
440216d0fdeSFlorian Fainelli udelay(1000);
441216d0fdeSFlorian Fainelli __raw_writel(r | USBHEN_CE | USBHEN_E, base + creg);
442216d0fdeSFlorian Fainelli wmb();
443216d0fdeSFlorian Fainelli udelay(1000);
444216d0fdeSFlorian Fainelli
445216d0fdeSFlorian Fainelli /* wait for reset complete (read reg twice: au1500 erratum) */
446216d0fdeSFlorian Fainelli while (__raw_readl(base + creg),
447216d0fdeSFlorian Fainelli !(__raw_readl(base + creg) & USBHEN_RD))
448216d0fdeSFlorian Fainelli udelay(1000);
449216d0fdeSFlorian Fainelli } else {
450216d0fdeSFlorian Fainelli __raw_writel(r & ~(USBHEN_CE | USBHEN_E), base + creg);
451216d0fdeSFlorian Fainelli wmb();
4523feae784SManuel Lauss clk_disable_unprepare(c);
453216d0fdeSFlorian Fainelli }
4543feae784SManuel Lauss out:
4553feae784SManuel Lauss clk_put(c);
456216d0fdeSFlorian Fainelli }
457216d0fdeSFlorian Fainelli
au1000_usb_control(int block,int enable,unsigned long rb,int creg)458216d0fdeSFlorian Fainelli static inline int au1000_usb_control(int block, int enable, unsigned long rb,
459216d0fdeSFlorian Fainelli int creg)
460216d0fdeSFlorian Fainelli {
461216d0fdeSFlorian Fainelli int ret = 0;
462216d0fdeSFlorian Fainelli
463216d0fdeSFlorian Fainelli switch (block) {
464216d0fdeSFlorian Fainelli case ALCHEMY_USB_OHCI0:
465216d0fdeSFlorian Fainelli __au1xx0_ohci_control(enable, rb, creg);
466216d0fdeSFlorian Fainelli break;
467216d0fdeSFlorian Fainelli default:
468216d0fdeSFlorian Fainelli ret = -ENODEV;
469216d0fdeSFlorian Fainelli }
470216d0fdeSFlorian Fainelli return ret;
471216d0fdeSFlorian Fainelli }
472216d0fdeSFlorian Fainelli
473216d0fdeSFlorian Fainelli /*
474216d0fdeSFlorian Fainelli * alchemy_usb_control - control Alchemy on-chip USB blocks
475216d0fdeSFlorian Fainelli * @block: USB block to target
476216d0fdeSFlorian Fainelli * @enable: set 1 to enable a block, 0 to disable
477216d0fdeSFlorian Fainelli */
alchemy_usb_control(int block,int enable)478216d0fdeSFlorian Fainelli int alchemy_usb_control(int block, int enable)
479216d0fdeSFlorian Fainelli {
480216d0fdeSFlorian Fainelli unsigned long flags;
481216d0fdeSFlorian Fainelli int ret;
482216d0fdeSFlorian Fainelli
483216d0fdeSFlorian Fainelli spin_lock_irqsave(&alchemy_usb_lock, flags);
484216d0fdeSFlorian Fainelli switch (alchemy_get_cputype()) {
485216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1000:
486216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1500:
487216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1100:
488216d0fdeSFlorian Fainelli ret = au1000_usb_control(block, enable,
489216d0fdeSFlorian Fainelli AU1000_USB_OHCI_PHYS_ADDR, AU1000_OHCICFG);
490216d0fdeSFlorian Fainelli break;
491216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1550:
492216d0fdeSFlorian Fainelli ret = au1000_usb_control(block, enable,
493216d0fdeSFlorian Fainelli AU1550_USB_OHCI_PHYS_ADDR, AU1550_OHCICFG);
494216d0fdeSFlorian Fainelli break;
495216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1200:
496216d0fdeSFlorian Fainelli ret = au1200_usb_control(block, enable);
497216d0fdeSFlorian Fainelli break;
498216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1300:
499216d0fdeSFlorian Fainelli ret = au1300_usb_control(block, enable);
500216d0fdeSFlorian Fainelli break;
501216d0fdeSFlorian Fainelli default:
502216d0fdeSFlorian Fainelli ret = -ENODEV;
503216d0fdeSFlorian Fainelli }
504216d0fdeSFlorian Fainelli spin_unlock_irqrestore(&alchemy_usb_lock, flags);
505216d0fdeSFlorian Fainelli return ret;
506216d0fdeSFlorian Fainelli }
507216d0fdeSFlorian Fainelli EXPORT_SYMBOL_GPL(alchemy_usb_control);
508216d0fdeSFlorian Fainelli
509216d0fdeSFlorian Fainelli
510216d0fdeSFlorian Fainelli static unsigned long alchemy_usb_pmdata[2];
511216d0fdeSFlorian Fainelli
au1000_usb_pm(unsigned long br,int creg,int susp)512216d0fdeSFlorian Fainelli static void au1000_usb_pm(unsigned long br, int creg, int susp)
513216d0fdeSFlorian Fainelli {
514216d0fdeSFlorian Fainelli void __iomem *base = (void __iomem *)KSEG1ADDR(br);
515216d0fdeSFlorian Fainelli
516216d0fdeSFlorian Fainelli if (susp) {
517216d0fdeSFlorian Fainelli alchemy_usb_pmdata[0] = __raw_readl(base + creg);
518216d0fdeSFlorian Fainelli /* There appears to be some undocumented reset register.... */
519216d0fdeSFlorian Fainelli __raw_writel(0, base + 0x04);
520216d0fdeSFlorian Fainelli wmb();
521216d0fdeSFlorian Fainelli __raw_writel(0, base + creg);
522216d0fdeSFlorian Fainelli wmb();
523216d0fdeSFlorian Fainelli } else {
524216d0fdeSFlorian Fainelli __raw_writel(alchemy_usb_pmdata[0], base + creg);
525216d0fdeSFlorian Fainelli wmb();
526216d0fdeSFlorian Fainelli }
527216d0fdeSFlorian Fainelli }
528216d0fdeSFlorian Fainelli
au1200_usb_pm(int susp)529216d0fdeSFlorian Fainelli static void au1200_usb_pm(int susp)
530216d0fdeSFlorian Fainelli {
531216d0fdeSFlorian Fainelli void __iomem *base =
532216d0fdeSFlorian Fainelli (void __iomem *)KSEG1ADDR(AU1200_USB_OTG_PHYS_ADDR);
533216d0fdeSFlorian Fainelli if (susp) {
534216d0fdeSFlorian Fainelli /* save OTG_CAP/MUX registers which indicate port routing */
535216d0fdeSFlorian Fainelli /* FIXME: write an OTG driver to do that */
536216d0fdeSFlorian Fainelli alchemy_usb_pmdata[0] = __raw_readl(base + 0x00);
537216d0fdeSFlorian Fainelli alchemy_usb_pmdata[1] = __raw_readl(base + 0x04);
538216d0fdeSFlorian Fainelli } else {
539216d0fdeSFlorian Fainelli /* restore access to all MMIO areas */
540216d0fdeSFlorian Fainelli au1200_usb_init();
541216d0fdeSFlorian Fainelli
542216d0fdeSFlorian Fainelli /* restore OTG_CAP/MUX registers */
543216d0fdeSFlorian Fainelli __raw_writel(alchemy_usb_pmdata[0], base + 0x00);
544216d0fdeSFlorian Fainelli __raw_writel(alchemy_usb_pmdata[1], base + 0x04);
545216d0fdeSFlorian Fainelli wmb();
546216d0fdeSFlorian Fainelli }
547216d0fdeSFlorian Fainelli }
548216d0fdeSFlorian Fainelli
au1300_usb_pm(int susp)549216d0fdeSFlorian Fainelli static void au1300_usb_pm(int susp)
550216d0fdeSFlorian Fainelli {
551216d0fdeSFlorian Fainelli void __iomem *base =
552216d0fdeSFlorian Fainelli (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR);
553216d0fdeSFlorian Fainelli /* remember Port2 routing */
554216d0fdeSFlorian Fainelli if (susp) {
555216d0fdeSFlorian Fainelli alchemy_usb_pmdata[0] = __raw_readl(base + USB_DWC_CTRL4);
556216d0fdeSFlorian Fainelli } else {
557216d0fdeSFlorian Fainelli au1300_usb_init();
558216d0fdeSFlorian Fainelli __raw_writel(alchemy_usb_pmdata[0], base + USB_DWC_CTRL4);
559216d0fdeSFlorian Fainelli wmb();
560216d0fdeSFlorian Fainelli }
561216d0fdeSFlorian Fainelli }
562216d0fdeSFlorian Fainelli
alchemy_usb_pm(int susp)563216d0fdeSFlorian Fainelli static void alchemy_usb_pm(int susp)
564216d0fdeSFlorian Fainelli {
565216d0fdeSFlorian Fainelli switch (alchemy_get_cputype()) {
566216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1000:
567216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1500:
568216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1100:
569216d0fdeSFlorian Fainelli au1000_usb_pm(AU1000_USB_OHCI_PHYS_ADDR, AU1000_OHCICFG, susp);
570216d0fdeSFlorian Fainelli break;
571216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1550:
572216d0fdeSFlorian Fainelli au1000_usb_pm(AU1550_USB_OHCI_PHYS_ADDR, AU1550_OHCICFG, susp);
573216d0fdeSFlorian Fainelli break;
574216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1200:
575216d0fdeSFlorian Fainelli au1200_usb_pm(susp);
576216d0fdeSFlorian Fainelli break;
577216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1300:
578216d0fdeSFlorian Fainelli au1300_usb_pm(susp);
579216d0fdeSFlorian Fainelli break;
580216d0fdeSFlorian Fainelli }
581216d0fdeSFlorian Fainelli }
582216d0fdeSFlorian Fainelli
alchemy_usb_suspend(void)583216d0fdeSFlorian Fainelli static int alchemy_usb_suspend(void)
584216d0fdeSFlorian Fainelli {
585216d0fdeSFlorian Fainelli alchemy_usb_pm(1);
586216d0fdeSFlorian Fainelli return 0;
587216d0fdeSFlorian Fainelli }
588216d0fdeSFlorian Fainelli
alchemy_usb_resume(void)589216d0fdeSFlorian Fainelli static void alchemy_usb_resume(void)
590216d0fdeSFlorian Fainelli {
591216d0fdeSFlorian Fainelli alchemy_usb_pm(0);
592216d0fdeSFlorian Fainelli }
593216d0fdeSFlorian Fainelli
594216d0fdeSFlorian Fainelli static struct syscore_ops alchemy_usb_pm_ops = {
595216d0fdeSFlorian Fainelli .suspend = alchemy_usb_suspend,
596216d0fdeSFlorian Fainelli .resume = alchemy_usb_resume,
597216d0fdeSFlorian Fainelli };
598216d0fdeSFlorian Fainelli
alchemy_usb_init(void)599216d0fdeSFlorian Fainelli static int __init alchemy_usb_init(void)
600216d0fdeSFlorian Fainelli {
6013feae784SManuel Lauss int ret = 0;
6023feae784SManuel Lauss
603216d0fdeSFlorian Fainelli switch (alchemy_get_cputype()) {
604216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1000:
605216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1500:
606216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1100:
6073feae784SManuel Lauss ret = au1000_usb_init(AU1000_USB_OHCI_PHYS_ADDR,
6083feae784SManuel Lauss AU1000_OHCICFG);
609216d0fdeSFlorian Fainelli break;
610216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1550:
6113feae784SManuel Lauss ret = au1000_usb_init(AU1550_USB_OHCI_PHYS_ADDR,
6123feae784SManuel Lauss AU1550_OHCICFG);
613216d0fdeSFlorian Fainelli break;
614216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1200:
615216d0fdeSFlorian Fainelli au1200_usb_init();
616216d0fdeSFlorian Fainelli break;
617216d0fdeSFlorian Fainelli case ALCHEMY_CPU_AU1300:
618216d0fdeSFlorian Fainelli au1300_usb_init();
619216d0fdeSFlorian Fainelli break;
620216d0fdeSFlorian Fainelli }
621216d0fdeSFlorian Fainelli
6223feae784SManuel Lauss if (!ret)
623216d0fdeSFlorian Fainelli register_syscore_ops(&alchemy_usb_pm_ops);
624216d0fdeSFlorian Fainelli
6253feae784SManuel Lauss return ret;
626216d0fdeSFlorian Fainelli }
627216d0fdeSFlorian Fainelli arch_initcall(alchemy_usb_init);
628