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