diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/mips/au1000/common |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/mips/au1000/common')
-rw-r--r-- | arch/mips/au1000/common/Makefile | 15 | ||||
-rw-r--r-- | arch/mips/au1000/common/au1xxx_irqmap.c | 224 | ||||
-rw-r--r-- | arch/mips/au1000/common/clocks.c | 96 | ||||
-rw-r--r-- | arch/mips/au1000/common/cputable.c | 55 | ||||
-rw-r--r-- | arch/mips/au1000/common/dbdma.c | 836 | ||||
-rw-r--r-- | arch/mips/au1000/common/dbg_io.c | 122 | ||||
-rw-r--r-- | arch/mips/au1000/common/dma.c | 243 | ||||
-rw-r--r-- | arch/mips/au1000/common/int-handler.S | 68 | ||||
-rw-r--r-- | arch/mips/au1000/common/irq.c | 654 | ||||
-rw-r--r-- | arch/mips/au1000/common/pci.c | 97 | ||||
-rw-r--r-- | arch/mips/au1000/common/platform.c | 53 | ||||
-rw-r--r-- | arch/mips/au1000/common/power.c | 493 | ||||
-rw-r--r-- | arch/mips/au1000/common/prom.c | 162 | ||||
-rw-r--r-- | arch/mips/au1000/common/puts.c | 145 | ||||
-rw-r--r-- | arch/mips/au1000/common/reset.c | 195 | ||||
-rw-r--r-- | arch/mips/au1000/common/setup.c | 195 | ||||
-rw-r--r-- | arch/mips/au1000/common/sleeper.S | 149 | ||||
-rw-r--r-- | arch/mips/au1000/common/time.c | 469 | ||||
-rw-r--r-- | arch/mips/au1000/common/usbdev.c | 1557 |
19 files changed, 5828 insertions, 0 deletions
diff --git a/arch/mips/au1000/common/Makefile b/arch/mips/au1000/common/Makefile new file mode 100644 index 000000000000..594b75e5e080 --- /dev/null +++ b/arch/mips/au1000/common/Makefile | |||
@@ -0,0 +1,15 @@ | |||
1 | # | ||
2 | # Copyright 2000 MontaVista Software Inc. | ||
3 | # Author: MontaVista Software, Inc. | ||
4 | # ppopov@mvista.com or source@mvista.com | ||
5 | # | ||
6 | # Makefile for the Alchemy Au1000 CPU, generic files. | ||
7 | # | ||
8 | |||
9 | obj-y += prom.o int-handler.o irq.o puts.o time.o reset.o \ | ||
10 | au1xxx_irqmap.o clocks.o platform.o power.o setup.o \ | ||
11 | sleeper.o cputable.o dma.o dbdma.o | ||
12 | |||
13 | obj-$(CONFIG_AU1X00_USB_DEVICE) += usbdev.o | ||
14 | obj-$(CONFIG_KGDB) += dbg_io.o | ||
15 | obj-$(CONFIG_PCI) += pci.o | ||
diff --git a/arch/mips/au1000/common/au1xxx_irqmap.c b/arch/mips/au1000/common/au1xxx_irqmap.c new file mode 100644 index 000000000000..8a0f39f67c59 --- /dev/null +++ b/arch/mips/au1000/common/au1xxx_irqmap.c | |||
@@ -0,0 +1,224 @@ | |||
1 | /* | ||
2 | * BRIEF MODULE DESCRIPTION | ||
3 | * Au1xxx processor specific IRQ tables | ||
4 | * | ||
5 | * Copyright 2004 Embedded Edge, LLC | ||
6 | * dan@embeddededge.com | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | * | ||
13 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
14 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
15 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
16 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
19 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
20 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License along | ||
25 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
26 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
27 | */ | ||
28 | #include <linux/config.h> | ||
29 | #include <linux/errno.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/irq.h> | ||
32 | #include <linux/kernel_stat.h> | ||
33 | #include <linux/module.h> | ||
34 | #include <linux/signal.h> | ||
35 | #include <linux/sched.h> | ||
36 | #include <linux/types.h> | ||
37 | #include <linux/interrupt.h> | ||
38 | #include <linux/ioport.h> | ||
39 | #include <linux/timex.h> | ||
40 | #include <linux/slab.h> | ||
41 | #include <linux/random.h> | ||
42 | #include <linux/delay.h> | ||
43 | #include <linux/bitops.h> | ||
44 | |||
45 | #include <asm/bootinfo.h> | ||
46 | #include <asm/io.h> | ||
47 | #include <asm/mipsregs.h> | ||
48 | #include <asm/system.h> | ||
49 | #include <asm/mach-au1x00/au1000.h> | ||
50 | |||
51 | /* The IC0 interrupt table. This is processor, rather than | ||
52 | * board dependent, so no reason to keep this info in the board | ||
53 | * dependent files. | ||
54 | * | ||
55 | * Careful if you change match 2 request! | ||
56 | * The interrupt handler is called directly from the low level dispatch code. | ||
57 | */ | ||
58 | au1xxx_irq_map_t au1xxx_ic0_map[] = { | ||
59 | |||
60 | #if defined(CONFIG_SOC_AU1000) | ||
61 | { AU1000_UART0_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
62 | { AU1000_UART1_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
63 | { AU1000_UART2_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
64 | { AU1000_UART3_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
65 | { AU1000_SSI0_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
66 | { AU1000_SSI1_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
67 | { AU1000_DMA_INT_BASE, INTC_INT_HIGH_LEVEL, 0}, | ||
68 | { AU1000_DMA_INT_BASE+1, INTC_INT_HIGH_LEVEL, 0}, | ||
69 | { AU1000_DMA_INT_BASE+2, INTC_INT_HIGH_LEVEL, 0}, | ||
70 | { AU1000_DMA_INT_BASE+3, INTC_INT_HIGH_LEVEL, 0}, | ||
71 | { AU1000_DMA_INT_BASE+4, INTC_INT_HIGH_LEVEL, 0}, | ||
72 | { AU1000_DMA_INT_BASE+5, INTC_INT_HIGH_LEVEL, 0}, | ||
73 | { AU1000_DMA_INT_BASE+6, INTC_INT_HIGH_LEVEL, 0}, | ||
74 | { AU1000_DMA_INT_BASE+7, INTC_INT_HIGH_LEVEL, 0}, | ||
75 | { AU1000_TOY_INT, INTC_INT_RISE_EDGE, 0 }, | ||
76 | { AU1000_TOY_MATCH0_INT, INTC_INT_RISE_EDGE, 0 }, | ||
77 | { AU1000_TOY_MATCH1_INT, INTC_INT_RISE_EDGE, 0 }, | ||
78 | { AU1000_TOY_MATCH2_INT, INTC_INT_RISE_EDGE, 1 }, | ||
79 | { AU1000_RTC_INT, INTC_INT_RISE_EDGE, 0 }, | ||
80 | { AU1000_RTC_MATCH0_INT, INTC_INT_RISE_EDGE, 0 }, | ||
81 | { AU1000_RTC_MATCH1_INT, INTC_INT_RISE_EDGE, 0 }, | ||
82 | { AU1000_RTC_MATCH2_INT, INTC_INT_RISE_EDGE, 0 }, | ||
83 | { AU1000_IRDA_TX_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
84 | { AU1000_IRDA_RX_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
85 | { AU1000_USB_DEV_REQ_INT, INTC_INT_HIGH_LEVEL, 0 }, | ||
86 | { AU1000_USB_DEV_SUS_INT, INTC_INT_RISE_EDGE, 0 }, | ||
87 | { AU1000_USB_HOST_INT, INTC_INT_LOW_LEVEL, 0 }, | ||
88 | { AU1000_ACSYNC_INT, INTC_INT_RISE_EDGE, 0 }, | ||
89 | { AU1000_MAC0_DMA_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
90 | { AU1000_MAC1_DMA_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
91 | { AU1000_AC97C_INT, INTC_INT_RISE_EDGE, 0 }, | ||
92 | |||
93 | #elif defined(CONFIG_SOC_AU1500) | ||
94 | |||
95 | { AU1500_UART0_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
96 | { AU1000_PCI_INTA, INTC_INT_LOW_LEVEL, 0 }, | ||
97 | { AU1000_PCI_INTB, INTC_INT_LOW_LEVEL, 0 }, | ||
98 | { AU1500_UART3_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
99 | { AU1000_PCI_INTC, INTC_INT_LOW_LEVEL, 0 }, | ||
100 | { AU1000_PCI_INTD, INTC_INT_LOW_LEVEL, 0 }, | ||
101 | { AU1000_DMA_INT_BASE, INTC_INT_HIGH_LEVEL, 0}, | ||
102 | { AU1000_DMA_INT_BASE+1, INTC_INT_HIGH_LEVEL, 0}, | ||
103 | { AU1000_DMA_INT_BASE+2, INTC_INT_HIGH_LEVEL, 0}, | ||
104 | { AU1000_DMA_INT_BASE+3, INTC_INT_HIGH_LEVEL, 0}, | ||
105 | { AU1000_DMA_INT_BASE+4, INTC_INT_HIGH_LEVEL, 0}, | ||
106 | { AU1000_DMA_INT_BASE+5, INTC_INT_HIGH_LEVEL, 0}, | ||
107 | { AU1000_DMA_INT_BASE+6, INTC_INT_HIGH_LEVEL, 0}, | ||
108 | { AU1000_DMA_INT_BASE+7, INTC_INT_HIGH_LEVEL, 0}, | ||
109 | { AU1000_TOY_INT, INTC_INT_RISE_EDGE, 0 }, | ||
110 | { AU1000_TOY_MATCH0_INT, INTC_INT_RISE_EDGE, 0 }, | ||
111 | { AU1000_TOY_MATCH1_INT, INTC_INT_RISE_EDGE, 0 }, | ||
112 | { AU1000_TOY_MATCH2_INT, INTC_INT_RISE_EDGE, 1 }, | ||
113 | { AU1000_RTC_INT, INTC_INT_RISE_EDGE, 0 }, | ||
114 | { AU1000_RTC_MATCH0_INT, INTC_INT_RISE_EDGE, 0 }, | ||
115 | { AU1000_RTC_MATCH1_INT, INTC_INT_RISE_EDGE, 0 }, | ||
116 | { AU1000_RTC_MATCH2_INT, INTC_INT_RISE_EDGE, 0 }, | ||
117 | { AU1000_USB_DEV_REQ_INT, INTC_INT_HIGH_LEVEL, 0 }, | ||
118 | { AU1000_USB_DEV_SUS_INT, INTC_INT_RISE_EDGE, 0 }, | ||
119 | { AU1000_USB_HOST_INT, INTC_INT_LOW_LEVEL, 0 }, | ||
120 | { AU1000_ACSYNC_INT, INTC_INT_RISE_EDGE, 0 }, | ||
121 | { AU1500_MAC0_DMA_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
122 | { AU1500_MAC1_DMA_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
123 | { AU1000_AC97C_INT, INTC_INT_RISE_EDGE, 0 }, | ||
124 | |||
125 | #elif defined(CONFIG_SOC_AU1100) | ||
126 | |||
127 | { AU1100_UART0_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
128 | { AU1100_UART1_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
129 | { AU1100_SD_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
130 | { AU1100_UART3_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
131 | { AU1000_SSI0_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
132 | { AU1000_SSI1_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
133 | { AU1000_DMA_INT_BASE, INTC_INT_HIGH_LEVEL, 0}, | ||
134 | { AU1000_DMA_INT_BASE+1, INTC_INT_HIGH_LEVEL, 0}, | ||
135 | { AU1000_DMA_INT_BASE+2, INTC_INT_HIGH_LEVEL, 0}, | ||
136 | { AU1000_DMA_INT_BASE+3, INTC_INT_HIGH_LEVEL, 0}, | ||
137 | { AU1000_DMA_INT_BASE+4, INTC_INT_HIGH_LEVEL, 0}, | ||
138 | { AU1000_DMA_INT_BASE+5, INTC_INT_HIGH_LEVEL, 0}, | ||
139 | { AU1000_DMA_INT_BASE+6, INTC_INT_HIGH_LEVEL, 0}, | ||
140 | { AU1000_DMA_INT_BASE+7, INTC_INT_HIGH_LEVEL, 0}, | ||
141 | { AU1000_TOY_INT, INTC_INT_RISE_EDGE, 0 }, | ||
142 | { AU1000_TOY_MATCH0_INT, INTC_INT_RISE_EDGE, 0 }, | ||
143 | { AU1000_TOY_MATCH1_INT, INTC_INT_RISE_EDGE, 0 }, | ||
144 | { AU1000_TOY_MATCH2_INT, INTC_INT_RISE_EDGE, 1 }, | ||
145 | { AU1000_RTC_INT, INTC_INT_RISE_EDGE, 0 }, | ||
146 | { AU1000_RTC_MATCH0_INT, INTC_INT_RISE_EDGE, 0 }, | ||
147 | { AU1000_RTC_MATCH1_INT, INTC_INT_RISE_EDGE, 0 }, | ||
148 | { AU1000_RTC_MATCH2_INT, INTC_INT_RISE_EDGE, 0 }, | ||
149 | { AU1000_IRDA_TX_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
150 | { AU1000_IRDA_RX_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
151 | { AU1000_USB_DEV_REQ_INT, INTC_INT_HIGH_LEVEL, 0 }, | ||
152 | { AU1000_USB_DEV_SUS_INT, INTC_INT_RISE_EDGE, 0 }, | ||
153 | { AU1000_USB_HOST_INT, INTC_INT_LOW_LEVEL, 0 }, | ||
154 | { AU1000_ACSYNC_INT, INTC_INT_RISE_EDGE, 0 }, | ||
155 | { AU1100_MAC0_DMA_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
156 | /*{ AU1000_GPIO215_208_INT, INTC_INT_HIGH_LEVEL, 0},*/ | ||
157 | { AU1100_LCD_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
158 | { AU1000_AC97C_INT, INTC_INT_RISE_EDGE, 0 }, | ||
159 | |||
160 | #elif defined(CONFIG_SOC_AU1550) | ||
161 | |||
162 | { AU1550_UART0_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
163 | { AU1550_PCI_INTA, INTC_INT_LOW_LEVEL, 0 }, | ||
164 | { AU1550_PCI_INTB, INTC_INT_LOW_LEVEL, 0 }, | ||
165 | { AU1550_DDMA_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
166 | { AU1550_CRYPTO_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
167 | { AU1550_PCI_INTC, INTC_INT_LOW_LEVEL, 0 }, | ||
168 | { AU1550_PCI_INTD, INTC_INT_LOW_LEVEL, 0 }, | ||
169 | { AU1550_PCI_RST_INT, INTC_INT_LOW_LEVEL, 0 }, | ||
170 | { AU1550_UART1_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
171 | { AU1550_UART3_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
172 | { AU1550_PSC0_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
173 | { AU1550_PSC1_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
174 | { AU1550_PSC2_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
175 | { AU1550_PSC3_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
176 | { AU1550_TOY_INT, INTC_INT_RISE_EDGE, 0 }, | ||
177 | { AU1550_TOY_MATCH0_INT, INTC_INT_RISE_EDGE, 0 }, | ||
178 | { AU1550_TOY_MATCH1_INT, INTC_INT_RISE_EDGE, 0 }, | ||
179 | { AU1550_TOY_MATCH2_INT, INTC_INT_RISE_EDGE, 1 }, | ||
180 | { AU1550_RTC_INT, INTC_INT_RISE_EDGE, 0 }, | ||
181 | { AU1550_RTC_MATCH0_INT, INTC_INT_RISE_EDGE, 0 }, | ||
182 | { AU1550_RTC_MATCH1_INT, INTC_INT_RISE_EDGE, 0 }, | ||
183 | { AU1550_RTC_MATCH2_INT, INTC_INT_RISE_EDGE, 0 }, | ||
184 | { AU1550_NAND_INT, INTC_INT_RISE_EDGE, 0}, | ||
185 | { AU1550_USB_DEV_REQ_INT, INTC_INT_HIGH_LEVEL, 0 }, | ||
186 | { AU1550_USB_DEV_SUS_INT, INTC_INT_RISE_EDGE, 0 }, | ||
187 | { AU1550_USB_HOST_INT, INTC_INT_LOW_LEVEL, 0 }, | ||
188 | { AU1550_MAC0_DMA_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
189 | { AU1550_MAC1_DMA_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
190 | |||
191 | #elif defined(CONFIG_SOC_AU1200) | ||
192 | |||
193 | { AU1200_UART0_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
194 | { AU1200_SWT_INT, INTC_INT_RISE_EDGE, 0 }, | ||
195 | { AU1200_SD_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
196 | { AU1200_DDMA_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
197 | { AU1200_MAE_BE_INT, INTC_INT_HIGH_LEVEL, 0 }, | ||
198 | { AU1200_UART1_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
199 | { AU1200_MAE_FE_INT, INTC_INT_HIGH_LEVEL, 0 }, | ||
200 | { AU1200_PSC0_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
201 | { AU1200_PSC1_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
202 | { AU1200_AES_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
203 | { AU1200_CAMERA_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
204 | { AU1200_TOY_INT, INTC_INT_RISE_EDGE, 0 }, | ||
205 | { AU1200_TOY_MATCH0_INT, INTC_INT_RISE_EDGE, 0 }, | ||
206 | { AU1200_TOY_MATCH1_INT, INTC_INT_RISE_EDGE, 0 }, | ||
207 | { AU1200_TOY_MATCH2_INT, INTC_INT_RISE_EDGE, 1 }, | ||
208 | { AU1200_RTC_INT, INTC_INT_RISE_EDGE, 0 }, | ||
209 | { AU1200_RTC_MATCH0_INT, INTC_INT_RISE_EDGE, 0 }, | ||
210 | { AU1200_RTC_MATCH1_INT, INTC_INT_RISE_EDGE, 0 }, | ||
211 | { AU1200_RTC_MATCH2_INT, INTC_INT_RISE_EDGE, 0 }, | ||
212 | { AU1200_NAND_INT, INTC_INT_RISE_EDGE, 0}, | ||
213 | { AU1200_USB_INT, INTC_INT_HIGH_LEVEL, 0 }, | ||
214 | { AU1200_LCD_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
215 | { AU1200_MAE_BOTH_INT, INTC_INT_HIGH_LEVEL, 0}, | ||
216 | |||
217 | #else | ||
218 | #error "Error: Unknown Alchemy SOC" | ||
219 | #endif | ||
220 | |||
221 | }; | ||
222 | |||
223 | int au1xxx_ic0_nr_irqs = sizeof(au1xxx_ic0_map)/sizeof(au1xxx_irq_map_t); | ||
224 | |||
diff --git a/arch/mips/au1000/common/clocks.c b/arch/mips/au1000/common/clocks.c new file mode 100644 index 000000000000..3ce6cace0eb0 --- /dev/null +++ b/arch/mips/au1000/common/clocks.c | |||
@@ -0,0 +1,96 @@ | |||
1 | /* | ||
2 | * BRIEF MODULE DESCRIPTION | ||
3 | * Simple Au1000 clocks routines. | ||
4 | * | ||
5 | * Copyright 2001 MontaVista Software Inc. | ||
6 | * Author: MontaVista Software, Inc. | ||
7 | * ppopov@mvista.com or source@mvista.com | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License as published by the | ||
11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
12 | * option) any later version. | ||
13 | * | ||
14 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
15 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
16 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
17 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
20 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
21 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
24 | * | ||
25 | * You should have received a copy of the GNU General Public License along | ||
26 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
27 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
28 | */ | ||
29 | |||
30 | #include <linux/module.h> | ||
31 | #include <asm/mach-au1x00/au1000.h> | ||
32 | |||
33 | static unsigned int au1x00_clock; // Hz | ||
34 | static unsigned int lcd_clock; // KHz | ||
35 | static unsigned long uart_baud_base; | ||
36 | |||
37 | /* | ||
38 | * Set the au1000_clock | ||
39 | */ | ||
40 | void set_au1x00_speed(unsigned int new_freq) | ||
41 | { | ||
42 | au1x00_clock = new_freq; | ||
43 | } | ||
44 | |||
45 | unsigned int get_au1x00_speed(void) | ||
46 | { | ||
47 | return au1x00_clock; | ||
48 | } | ||
49 | |||
50 | |||
51 | |||
52 | /* | ||
53 | * The UART baud base is not known at compile time ... if | ||
54 | * we want to be able to use the same code on different | ||
55 | * speed CPUs. | ||
56 | */ | ||
57 | unsigned long get_au1x00_uart_baud_base(void) | ||
58 | { | ||
59 | return uart_baud_base; | ||
60 | } | ||
61 | |||
62 | void set_au1x00_uart_baud_base(unsigned long new_baud_base) | ||
63 | { | ||
64 | uart_baud_base = new_baud_base; | ||
65 | } | ||
66 | |||
67 | /* | ||
68 | * Calculate the Au1x00's LCD clock based on the current | ||
69 | * cpu clock and the system bus clock, and try to keep it | ||
70 | * below 40 MHz (the Pb1000 board can lock-up if the LCD | ||
71 | * clock is over 40 MHz). | ||
72 | */ | ||
73 | void set_au1x00_lcd_clock(void) | ||
74 | { | ||
75 | unsigned int static_cfg0; | ||
76 | unsigned int sys_busclk = | ||
77 | (get_au1x00_speed()/1000) / | ||
78 | ((int)(au_readl(SYS_POWERCTRL)&0x03) + 2); | ||
79 | |||
80 | static_cfg0 = au_readl(MEM_STCFG0); | ||
81 | |||
82 | if (static_cfg0 & (1<<11)) | ||
83 | lcd_clock = sys_busclk / 5; /* note: BCLK switching fails with D5 */ | ||
84 | else | ||
85 | lcd_clock = sys_busclk / 4; | ||
86 | |||
87 | if (lcd_clock > 50000) /* Epson MAX */ | ||
88 | printk("warning: LCD clock too high (%d KHz)\n", lcd_clock); | ||
89 | } | ||
90 | |||
91 | unsigned int get_au1x00_lcd_clock(void) | ||
92 | { | ||
93 | return lcd_clock; | ||
94 | } | ||
95 | |||
96 | EXPORT_SYMBOL(get_au1x00_lcd_clock); | ||
diff --git a/arch/mips/au1000/common/cputable.c b/arch/mips/au1000/common/cputable.c new file mode 100644 index 000000000000..f5521dfccfd6 --- /dev/null +++ b/arch/mips/au1000/common/cputable.c | |||
@@ -0,0 +1,55 @@ | |||
1 | /* | ||
2 | * arch/mips/au1000/common/cputable.c | ||
3 | * | ||
4 | * Copyright (C) 2004 Dan Malek (dan@embeddededge.com) | ||
5 | * Copied from PowerPC and updated for Alchemy Au1xxx processors. | ||
6 | * | ||
7 | * Copyright (C) 2001 Ben. Herrenschmidt (benh@kernel.crashing.org) | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * as published by the Free Software Foundation; either version | ||
12 | * 2 of the License, or (at your option) any later version. | ||
13 | */ | ||
14 | #include <linux/string.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/threads.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <asm/mach-au1x00/au1000.h> | ||
19 | |||
20 | struct cpu_spec* cur_cpu_spec[NR_CPUS]; | ||
21 | |||
22 | /* With some thought, we can probably use the mask to reduce the | ||
23 | * size of the table. | ||
24 | */ | ||
25 | struct cpu_spec cpu_specs[] = { | ||
26 | { 0xffffffff, 0x00030100, "Au1000 DA", 1, 0 }, | ||
27 | { 0xffffffff, 0x00030201, "Au1000 HA", 1, 0 }, | ||
28 | { 0xffffffff, 0x00030202, "Au1000 HB", 1, 0 }, | ||
29 | { 0xffffffff, 0x00030203, "Au1000 HC", 1, 1 }, | ||
30 | { 0xffffffff, 0x00030204, "Au1000 HD", 1, 1 }, | ||
31 | { 0xffffffff, 0x01030200, "Au1500 AB", 1, 1 }, | ||
32 | { 0xffffffff, 0x01030201, "Au1500 AC", 0, 1 }, | ||
33 | { 0xffffffff, 0x01030202, "Au1500 AD", 0, 1 }, | ||
34 | { 0xffffffff, 0x02030200, "Au1100 AB", 1, 1 }, | ||
35 | { 0xffffffff, 0x02030201, "Au1100 BA", 1, 1 }, | ||
36 | { 0xffffffff, 0x02030202, "Au1100 BC", 1, 1 }, | ||
37 | { 0xffffffff, 0x02030203, "Au1100 BD", 0, 1 }, | ||
38 | { 0xffffffff, 0x02030204, "Au1100 BE", 0, 1 }, | ||
39 | { 0xffffffff, 0x03030200, "Au1550 AA", 0, 1 }, | ||
40 | { 0xffffffff, 0x04030200, "Au1200 AA", 0, 1 }, | ||
41 | { 0x00000000, 0x00000000, "Unknown Au1xxx", 1, 0 }, | ||
42 | }; | ||
43 | |||
44 | void | ||
45 | set_cpuspec(void) | ||
46 | { | ||
47 | struct cpu_spec *sp; | ||
48 | u32 prid; | ||
49 | |||
50 | prid = read_c0_prid(); | ||
51 | sp = cpu_specs; | ||
52 | while ((prid & sp->prid_mask) != sp->prid_value) | ||
53 | sp++; | ||
54 | cur_cpu_spec[0] = sp; | ||
55 | } | ||
diff --git a/arch/mips/au1000/common/dbdma.c b/arch/mips/au1000/common/dbdma.c new file mode 100644 index 000000000000..adfc3172aace --- /dev/null +++ b/arch/mips/au1000/common/dbdma.c | |||
@@ -0,0 +1,836 @@ | |||
1 | /* | ||
2 | * | ||
3 | * BRIEF MODULE DESCRIPTION | ||
4 | * The Descriptor Based DMA channel manager that first appeared | ||
5 | * on the Au1550. I started with dma.c, but I think all that is | ||
6 | * left is this initial comment :-) | ||
7 | * | ||
8 | * Copyright 2004 Embedded Edge, LLC | ||
9 | * dan@embeddededge.com | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify it | ||
12 | * under the terms of the GNU General Public License as published by the | ||
13 | * Free Software Foundation; either version 2 of the License, or (at your | ||
14 | * option) any later version. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
19 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
21 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
22 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
23 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
25 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | * | ||
27 | * You should have received a copy of the GNU General Public License along | ||
28 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
29 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
30 | * | ||
31 | */ | ||
32 | #include <linux/config.h> | ||
33 | #include <linux/kernel.h> | ||
34 | #include <linux/errno.h> | ||
35 | #include <linux/sched.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/spinlock.h> | ||
38 | #include <linux/string.h> | ||
39 | #include <linux/delay.h> | ||
40 | #include <linux/interrupt.h> | ||
41 | #include <asm/mach-au1x00/au1000.h> | ||
42 | #include <asm/mach-au1x00/au1xxx_dbdma.h> | ||
43 | #include <asm/system.h> | ||
44 | |||
45 | #if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200) | ||
46 | |||
47 | /* | ||
48 | * The Descriptor Based DMA supports up to 16 channels. | ||
49 | * | ||
50 | * There are 32 devices defined. We keep an internal structure | ||
51 | * of devices using these channels, along with additional | ||
52 | * information. | ||
53 | * | ||
54 | * We allocate the descriptors and allow access to them through various | ||
55 | * functions. The drivers allocate the data buffers and assign them | ||
56 | * to the descriptors. | ||
57 | */ | ||
58 | static DEFINE_SPINLOCK(au1xxx_dbdma_spin_lock); | ||
59 | |||
60 | /* I couldn't find a macro that did this...... | ||
61 | */ | ||
62 | #define ALIGN_ADDR(x, a) ((((u32)(x)) + (a-1)) & ~(a-1)) | ||
63 | |||
64 | static volatile dbdma_global_t *dbdma_gptr = (dbdma_global_t *)DDMA_GLOBAL_BASE; | ||
65 | static int dbdma_initialized; | ||
66 | static void au1xxx_dbdma_init(void); | ||
67 | |||
68 | typedef struct dbdma_device_table { | ||
69 | u32 dev_id; | ||
70 | u32 dev_flags; | ||
71 | u32 dev_tsize; | ||
72 | u32 dev_devwidth; | ||
73 | u32 dev_physaddr; /* If FIFO */ | ||
74 | u32 dev_intlevel; | ||
75 | u32 dev_intpolarity; | ||
76 | } dbdev_tab_t; | ||
77 | |||
78 | typedef struct dbdma_chan_config { | ||
79 | u32 chan_flags; | ||
80 | u32 chan_index; | ||
81 | dbdev_tab_t *chan_src; | ||
82 | dbdev_tab_t *chan_dest; | ||
83 | au1x_dma_chan_t *chan_ptr; | ||
84 | au1x_ddma_desc_t *chan_desc_base; | ||
85 | au1x_ddma_desc_t *get_ptr, *put_ptr, *cur_ptr; | ||
86 | void *chan_callparam; | ||
87 | void (*chan_callback)(int, void *, struct pt_regs *); | ||
88 | } chan_tab_t; | ||
89 | |||
90 | #define DEV_FLAGS_INUSE (1 << 0) | ||
91 | #define DEV_FLAGS_ANYUSE (1 << 1) | ||
92 | #define DEV_FLAGS_OUT (1 << 2) | ||
93 | #define DEV_FLAGS_IN (1 << 3) | ||
94 | |||
95 | static dbdev_tab_t dbdev_tab[] = { | ||
96 | #ifdef CONFIG_SOC_AU1550 | ||
97 | /* UARTS */ | ||
98 | { DSCR_CMD0_UART0_TX, DEV_FLAGS_OUT, 0, 8, 0x11100004, 0, 0 }, | ||
99 | { DSCR_CMD0_UART0_RX, DEV_FLAGS_IN, 0, 8, 0x11100000, 0, 0 }, | ||
100 | { DSCR_CMD0_UART3_TX, DEV_FLAGS_OUT, 0, 8, 0x11400004, 0, 0 }, | ||
101 | { DSCR_CMD0_UART3_RX, DEV_FLAGS_IN, 0, 8, 0x11400000, 0, 0 }, | ||
102 | |||
103 | /* EXT DMA */ | ||
104 | { DSCR_CMD0_DMA_REQ0, 0, 0, 0, 0x00000000, 0, 0 }, | ||
105 | { DSCR_CMD0_DMA_REQ1, 0, 0, 0, 0x00000000, 0, 0 }, | ||
106 | { DSCR_CMD0_DMA_REQ2, 0, 0, 0, 0x00000000, 0, 0 }, | ||
107 | { DSCR_CMD0_DMA_REQ3, 0, 0, 0, 0x00000000, 0, 0 }, | ||
108 | |||
109 | /* USB DEV */ | ||
110 | { DSCR_CMD0_USBDEV_RX0, DEV_FLAGS_IN, 4, 8, 0x10200000, 0, 0 }, | ||
111 | { DSCR_CMD0_USBDEV_TX0, DEV_FLAGS_OUT, 4, 8, 0x10200004, 0, 0 }, | ||
112 | { DSCR_CMD0_USBDEV_TX1, DEV_FLAGS_OUT, 4, 8, 0x10200008, 0, 0 }, | ||
113 | { DSCR_CMD0_USBDEV_TX2, DEV_FLAGS_OUT, 4, 8, 0x1020000c, 0, 0 }, | ||
114 | { DSCR_CMD0_USBDEV_RX3, DEV_FLAGS_IN, 4, 8, 0x10200010, 0, 0 }, | ||
115 | { DSCR_CMD0_USBDEV_RX4, DEV_FLAGS_IN, 4, 8, 0x10200014, 0, 0 }, | ||
116 | |||
117 | /* PSC 0 */ | ||
118 | { DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 0, 0x11a0001c, 0, 0 }, | ||
119 | { DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 0, 0x11a0001c, 0, 0 }, | ||
120 | |||
121 | /* PSC 1 */ | ||
122 | { DSCR_CMD0_PSC1_TX, DEV_FLAGS_OUT, 0, 0, 0x11b0001c, 0, 0 }, | ||
123 | { DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 0, 0x11b0001c, 0, 0 }, | ||
124 | |||
125 | /* PSC 2 */ | ||
126 | { DSCR_CMD0_PSC2_TX, DEV_FLAGS_OUT, 0, 0, 0x10a0001c, 0, 0 }, | ||
127 | { DSCR_CMD0_PSC2_RX, DEV_FLAGS_IN, 0, 0, 0x10a0001c, 0, 0 }, | ||
128 | |||
129 | /* PSC 3 */ | ||
130 | { DSCR_CMD0_PSC3_TX, DEV_FLAGS_OUT, 0, 0, 0x10b0001c, 0, 0 }, | ||
131 | { DSCR_CMD0_PSC3_RX, DEV_FLAGS_IN, 0, 0, 0x10b0001c, 0, 0 }, | ||
132 | |||
133 | { DSCR_CMD0_PCI_WRITE, 0, 0, 0, 0x00000000, 0, 0 }, /* PCI */ | ||
134 | { DSCR_CMD0_NAND_FLASH, 0, 0, 0, 0x00000000, 0, 0 }, /* NAND */ | ||
135 | |||
136 | /* MAC 0 */ | ||
137 | { DSCR_CMD0_MAC0_RX, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, | ||
138 | { DSCR_CMD0_MAC0_TX, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 }, | ||
139 | |||
140 | /* MAC 1 */ | ||
141 | { DSCR_CMD0_MAC1_RX, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, | ||
142 | { DSCR_CMD0_MAC1_TX, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 }, | ||
143 | |||
144 | #endif /* CONFIG_SOC_AU1550 */ | ||
145 | |||
146 | #ifdef CONFIG_SOC_AU1200 | ||
147 | { DSCR_CMD0_UART0_TX, DEV_FLAGS_OUT, 0, 8, 0x11100004, 0, 0 }, | ||
148 | { DSCR_CMD0_UART0_RX, DEV_FLAGS_IN, 0, 8, 0x11100000, 0, 0 }, | ||
149 | { DSCR_CMD0_UART1_TX, DEV_FLAGS_OUT, 0, 8, 0x11200004, 0, 0 }, | ||
150 | { DSCR_CMD0_UART1_RX, DEV_FLAGS_IN, 0, 8, 0x11200000, 0, 0 }, | ||
151 | |||
152 | { DSCR_CMD0_DMA_REQ0, 0, 0, 0, 0x00000000, 0, 0 }, | ||
153 | { DSCR_CMD0_DMA_REQ1, 0, 0, 0, 0x00000000, 0, 0 }, | ||
154 | |||
155 | { DSCR_CMD0_MAE_BE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
156 | { DSCR_CMD0_MAE_FE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
157 | { DSCR_CMD0_MAE_BOTH, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
158 | { DSCR_CMD0_LCD, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
159 | |||
160 | { DSCR_CMD0_SDMS_TX0, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 }, | ||
161 | { DSCR_CMD0_SDMS_RX0, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, | ||
162 | { DSCR_CMD0_SDMS_TX1, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 }, | ||
163 | { DSCR_CMD0_SDMS_RX1, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, | ||
164 | |||
165 | { DSCR_CMD0_AES_TX, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 }, | ||
166 | { DSCR_CMD0_AES_RX, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, | ||
167 | |||
168 | { DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 0, 0x11a0001c, 0, 0 }, | ||
169 | { DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 0, 0x11a0001c, 0, 0 }, | ||
170 | { DSCR_CMD0_PSC0_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
171 | |||
172 | { DSCR_CMD0_PSC1_TX, DEV_FLAGS_OUT, 0, 0, 0x11b0001c, 0, 0 }, | ||
173 | { DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 0, 0x11b0001c, 0, 0 }, | ||
174 | { DSCR_CMD0_PSC1_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
175 | |||
176 | { DSCR_CMD0_CIM_RXA, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, | ||
177 | { DSCR_CMD0_CIM_RXB, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, | ||
178 | { DSCR_CMD0_CIM_RXC, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, | ||
179 | { DSCR_CMD0_CIM_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
180 | |||
181 | { DSCR_CMD0_NAND_FLASH, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, | ||
182 | |||
183 | #endif // CONFIG_SOC_AU1200 | ||
184 | |||
185 | { DSCR_CMD0_THROTTLE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
186 | { DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
187 | }; | ||
188 | |||
189 | #define DBDEV_TAB_SIZE (sizeof(dbdev_tab) / sizeof(dbdev_tab_t)) | ||
190 | |||
191 | static chan_tab_t *chan_tab_ptr[NUM_DBDMA_CHANS]; | ||
192 | |||
193 | static dbdev_tab_t * | ||
194 | find_dbdev_id (u32 id) | ||
195 | { | ||
196 | int i; | ||
197 | dbdev_tab_t *p; | ||
198 | for (i = 0; i < DBDEV_TAB_SIZE; ++i) { | ||
199 | p = &dbdev_tab[i]; | ||
200 | if (p->dev_id == id) | ||
201 | return p; | ||
202 | } | ||
203 | return NULL; | ||
204 | } | ||
205 | |||
206 | /* Allocate a channel and return a non-zero descriptor if successful. | ||
207 | */ | ||
208 | u32 | ||
209 | au1xxx_dbdma_chan_alloc(u32 srcid, u32 destid, | ||
210 | void (*callback)(int, void *, struct pt_regs *), void *callparam) | ||
211 | { | ||
212 | unsigned long flags; | ||
213 | u32 used, chan, rv; | ||
214 | u32 dcp; | ||
215 | int i; | ||
216 | dbdev_tab_t *stp, *dtp; | ||
217 | chan_tab_t *ctp; | ||
218 | volatile au1x_dma_chan_t *cp; | ||
219 | |||
220 | /* We do the intialization on the first channel allocation. | ||
221 | * We have to wait because of the interrupt handler initialization | ||
222 | * which can't be done successfully during board set up. | ||
223 | */ | ||
224 | if (!dbdma_initialized) | ||
225 | au1xxx_dbdma_init(); | ||
226 | dbdma_initialized = 1; | ||
227 | |||
228 | if ((srcid > DSCR_NDEV_IDS) || (destid > DSCR_NDEV_IDS)) | ||
229 | return 0; | ||
230 | |||
231 | if ((stp = find_dbdev_id(srcid)) == NULL) return 0; | ||
232 | if ((dtp = find_dbdev_id(destid)) == NULL) return 0; | ||
233 | |||
234 | used = 0; | ||
235 | rv = 0; | ||
236 | |||
237 | /* Check to see if we can get both channels. | ||
238 | */ | ||
239 | spin_lock_irqsave(&au1xxx_dbdma_spin_lock, flags); | ||
240 | if (!(stp->dev_flags & DEV_FLAGS_INUSE) || | ||
241 | (stp->dev_flags & DEV_FLAGS_ANYUSE)) { | ||
242 | /* Got source */ | ||
243 | stp->dev_flags |= DEV_FLAGS_INUSE; | ||
244 | if (!(dtp->dev_flags & DEV_FLAGS_INUSE) || | ||
245 | (dtp->dev_flags & DEV_FLAGS_ANYUSE)) { | ||
246 | /* Got destination */ | ||
247 | dtp->dev_flags |= DEV_FLAGS_INUSE; | ||
248 | } | ||
249 | else { | ||
250 | /* Can't get dest. Release src. | ||
251 | */ | ||
252 | stp->dev_flags &= ~DEV_FLAGS_INUSE; | ||
253 | used++; | ||
254 | } | ||
255 | } | ||
256 | else { | ||
257 | used++; | ||
258 | } | ||
259 | spin_unlock_irqrestore(&au1xxx_dbdma_spin_lock, flags); | ||
260 | |||
261 | if (!used) { | ||
262 | /* Let's see if we can allocate a channel for it. | ||
263 | */ | ||
264 | ctp = NULL; | ||
265 | chan = 0; | ||
266 | spin_lock_irqsave(&au1xxx_dbdma_spin_lock, flags); | ||
267 | for (i=0; i<NUM_DBDMA_CHANS; i++) { | ||
268 | if (chan_tab_ptr[i] == NULL) { | ||
269 | /* If kmalloc fails, it is caught below same | ||
270 | * as a channel not available. | ||
271 | */ | ||
272 | ctp = kmalloc(sizeof(chan_tab_t), GFP_KERNEL); | ||
273 | chan_tab_ptr[i] = ctp; | ||
274 | ctp->chan_index = chan = i; | ||
275 | break; | ||
276 | } | ||
277 | } | ||
278 | spin_unlock_irqrestore(&au1xxx_dbdma_spin_lock, flags); | ||
279 | |||
280 | if (ctp != NULL) { | ||
281 | memset(ctp, 0, sizeof(chan_tab_t)); | ||
282 | dcp = DDMA_CHANNEL_BASE; | ||
283 | dcp += (0x0100 * chan); | ||
284 | ctp->chan_ptr = (au1x_dma_chan_t *)dcp; | ||
285 | cp = (volatile au1x_dma_chan_t *)dcp; | ||
286 | ctp->chan_src = stp; | ||
287 | ctp->chan_dest = dtp; | ||
288 | ctp->chan_callback = callback; | ||
289 | ctp->chan_callparam = callparam; | ||
290 | |||
291 | /* Initialize channel configuration. | ||
292 | */ | ||
293 | i = 0; | ||
294 | if (stp->dev_intlevel) | ||
295 | i |= DDMA_CFG_SED; | ||
296 | if (stp->dev_intpolarity) | ||
297 | i |= DDMA_CFG_SP; | ||
298 | if (dtp->dev_intlevel) | ||
299 | i |= DDMA_CFG_DED; | ||
300 | if (dtp->dev_intpolarity) | ||
301 | i |= DDMA_CFG_DP; | ||
302 | cp->ddma_cfg = i; | ||
303 | au_sync(); | ||
304 | |||
305 | /* Return a non-zero value that can be used to | ||
306 | * find the channel information in subsequent | ||
307 | * operations. | ||
308 | */ | ||
309 | rv = (u32)(&chan_tab_ptr[chan]); | ||
310 | } | ||
311 | else { | ||
312 | /* Release devices. | ||
313 | */ | ||
314 | stp->dev_flags &= ~DEV_FLAGS_INUSE; | ||
315 | dtp->dev_flags &= ~DEV_FLAGS_INUSE; | ||
316 | } | ||
317 | } | ||
318 | return rv; | ||
319 | } | ||
320 | |||
321 | /* Set the device width if source or destination is a FIFO. | ||
322 | * Should be 8, 16, or 32 bits. | ||
323 | */ | ||
324 | u32 | ||
325 | au1xxx_dbdma_set_devwidth(u32 chanid, int bits) | ||
326 | { | ||
327 | u32 rv; | ||
328 | chan_tab_t *ctp; | ||
329 | dbdev_tab_t *stp, *dtp; | ||
330 | |||
331 | ctp = *((chan_tab_t **)chanid); | ||
332 | stp = ctp->chan_src; | ||
333 | dtp = ctp->chan_dest; | ||
334 | rv = 0; | ||
335 | |||
336 | if (stp->dev_flags & DEV_FLAGS_IN) { /* Source in fifo */ | ||
337 | rv = stp->dev_devwidth; | ||
338 | stp->dev_devwidth = bits; | ||
339 | } | ||
340 | if (dtp->dev_flags & DEV_FLAGS_OUT) { /* Destination out fifo */ | ||
341 | rv = dtp->dev_devwidth; | ||
342 | dtp->dev_devwidth = bits; | ||
343 | } | ||
344 | |||
345 | return rv; | ||
346 | } | ||
347 | |||
348 | /* Allocate a descriptor ring, initializing as much as possible. | ||
349 | */ | ||
350 | u32 | ||
351 | au1xxx_dbdma_ring_alloc(u32 chanid, int entries) | ||
352 | { | ||
353 | int i; | ||
354 | u32 desc_base, srcid, destid; | ||
355 | u32 cmd0, cmd1, src1, dest1; | ||
356 | u32 src0, dest0; | ||
357 | chan_tab_t *ctp; | ||
358 | dbdev_tab_t *stp, *dtp; | ||
359 | au1x_ddma_desc_t *dp; | ||
360 | |||
361 | /* I guess we could check this to be within the | ||
362 | * range of the table...... | ||
363 | */ | ||
364 | ctp = *((chan_tab_t **)chanid); | ||
365 | stp = ctp->chan_src; | ||
366 | dtp = ctp->chan_dest; | ||
367 | |||
368 | /* The descriptors must be 32-byte aligned. There is a | ||
369 | * possibility the allocation will give us such an address, | ||
370 | * and if we try that first we are likely to not waste larger | ||
371 | * slabs of memory. | ||
372 | */ | ||
373 | desc_base = (u32)kmalloc(entries * sizeof(au1x_ddma_desc_t), GFP_KERNEL); | ||
374 | if (desc_base == 0) | ||
375 | return 0; | ||
376 | |||
377 | if (desc_base & 0x1f) { | ||
378 | /* Lost....do it again, allocate extra, and round | ||
379 | * the address base. | ||
380 | */ | ||
381 | kfree((const void *)desc_base); | ||
382 | i = entries * sizeof(au1x_ddma_desc_t); | ||
383 | i += (sizeof(au1x_ddma_desc_t) - 1); | ||
384 | if ((desc_base = (u32)kmalloc(i, GFP_KERNEL)) == 0) | ||
385 | return 0; | ||
386 | |||
387 | desc_base = ALIGN_ADDR(desc_base, sizeof(au1x_ddma_desc_t)); | ||
388 | } | ||
389 | dp = (au1x_ddma_desc_t *)desc_base; | ||
390 | |||
391 | /* Keep track of the base descriptor. | ||
392 | */ | ||
393 | ctp->chan_desc_base = dp; | ||
394 | |||
395 | /* Initialize the rings with as much information as we know. | ||
396 | */ | ||
397 | srcid = stp->dev_id; | ||
398 | destid = dtp->dev_id; | ||
399 | |||
400 | cmd0 = cmd1 = src1 = dest1 = 0; | ||
401 | src0 = dest0 = 0; | ||
402 | |||
403 | cmd0 |= DSCR_CMD0_SID(srcid); | ||
404 | cmd0 |= DSCR_CMD0_DID(destid); | ||
405 | cmd0 |= DSCR_CMD0_IE | DSCR_CMD0_CV; | ||
406 | cmd0 |= DSCR_CMD0_ST(DSCR_CMD0_ST_CURRENT); | ||
407 | |||
408 | switch (stp->dev_devwidth) { | ||
409 | case 8: | ||
410 | cmd0 |= DSCR_CMD0_SW(DSCR_CMD0_BYTE); | ||
411 | break; | ||
412 | case 16: | ||
413 | cmd0 |= DSCR_CMD0_SW(DSCR_CMD0_HALFWORD); | ||
414 | break; | ||
415 | case 32: | ||
416 | default: | ||
417 | cmd0 |= DSCR_CMD0_SW(DSCR_CMD0_WORD); | ||
418 | break; | ||
419 | } | ||
420 | |||
421 | switch (dtp->dev_devwidth) { | ||
422 | case 8: | ||
423 | cmd0 |= DSCR_CMD0_DW(DSCR_CMD0_BYTE); | ||
424 | break; | ||
425 | case 16: | ||
426 | cmd0 |= DSCR_CMD0_DW(DSCR_CMD0_HALFWORD); | ||
427 | break; | ||
428 | case 32: | ||
429 | default: | ||
430 | cmd0 |= DSCR_CMD0_DW(DSCR_CMD0_WORD); | ||
431 | break; | ||
432 | } | ||
433 | |||
434 | /* If the device is marked as an in/out FIFO, ensure it is | ||
435 | * set non-coherent. | ||
436 | */ | ||
437 | if (stp->dev_flags & DEV_FLAGS_IN) | ||
438 | cmd0 |= DSCR_CMD0_SN; /* Source in fifo */ | ||
439 | if (dtp->dev_flags & DEV_FLAGS_OUT) | ||
440 | cmd0 |= DSCR_CMD0_DN; /* Destination out fifo */ | ||
441 | |||
442 | /* Set up source1. For now, assume no stride and increment. | ||
443 | * A channel attribute update can change this later. | ||
444 | */ | ||
445 | switch (stp->dev_tsize) { | ||
446 | case 1: | ||
447 | src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE1); | ||
448 | break; | ||
449 | case 2: | ||
450 | src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE2); | ||
451 | break; | ||
452 | case 4: | ||
453 | src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE4); | ||
454 | break; | ||
455 | case 8: | ||
456 | default: | ||
457 | src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE8); | ||
458 | break; | ||
459 | } | ||
460 | |||
461 | /* If source input is fifo, set static address. | ||
462 | */ | ||
463 | if (stp->dev_flags & DEV_FLAGS_IN) { | ||
464 | src0 = stp->dev_physaddr; | ||
465 | src1 |= DSCR_SRC1_SAM(DSCR_xAM_STATIC); | ||
466 | } | ||
467 | |||
468 | /* Set up dest1. For now, assume no stride and increment. | ||
469 | * A channel attribute update can change this later. | ||
470 | */ | ||
471 | switch (dtp->dev_tsize) { | ||
472 | case 1: | ||
473 | dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE1); | ||
474 | break; | ||
475 | case 2: | ||
476 | dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE2); | ||
477 | break; | ||
478 | case 4: | ||
479 | dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE4); | ||
480 | break; | ||
481 | case 8: | ||
482 | default: | ||
483 | dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE8); | ||
484 | break; | ||
485 | } | ||
486 | |||
487 | /* If destination output is fifo, set static address. | ||
488 | */ | ||
489 | if (dtp->dev_flags & DEV_FLAGS_OUT) { | ||
490 | dest0 = dtp->dev_physaddr; | ||
491 | dest1 |= DSCR_DEST1_DAM(DSCR_xAM_STATIC); | ||
492 | } | ||
493 | |||
494 | for (i=0; i<entries; i++) { | ||
495 | dp->dscr_cmd0 = cmd0; | ||
496 | dp->dscr_cmd1 = cmd1; | ||
497 | dp->dscr_source0 = src0; | ||
498 | dp->dscr_source1 = src1; | ||
499 | dp->dscr_dest0 = dest0; | ||
500 | dp->dscr_dest1 = dest1; | ||
501 | dp->dscr_stat = 0; | ||
502 | dp->dscr_nxtptr = DSCR_NXTPTR(virt_to_phys(dp + 1)); | ||
503 | dp++; | ||
504 | } | ||
505 | |||
506 | /* Make last descrptor point to the first. | ||
507 | */ | ||
508 | dp--; | ||
509 | dp->dscr_nxtptr = DSCR_NXTPTR(virt_to_phys(ctp->chan_desc_base)); | ||
510 | ctp->get_ptr = ctp->put_ptr = ctp->cur_ptr = ctp->chan_desc_base; | ||
511 | |||
512 | return (u32)(ctp->chan_desc_base); | ||
513 | } | ||
514 | |||
515 | /* Put a source buffer into the DMA ring. | ||
516 | * This updates the source pointer and byte count. Normally used | ||
517 | * for memory to fifo transfers. | ||
518 | */ | ||
519 | u32 | ||
520 | au1xxx_dbdma_put_source(u32 chanid, void *buf, int nbytes) | ||
521 | { | ||
522 | chan_tab_t *ctp; | ||
523 | au1x_ddma_desc_t *dp; | ||
524 | |||
525 | /* I guess we could check this to be within the | ||
526 | * range of the table...... | ||
527 | */ | ||
528 | ctp = *((chan_tab_t **)chanid); | ||
529 | |||
530 | /* We should have multiple callers for a particular channel, | ||
531 | * an interrupt doesn't affect this pointer nor the descriptor, | ||
532 | * so no locking should be needed. | ||
533 | */ | ||
534 | dp = ctp->put_ptr; | ||
535 | |||
536 | /* If the descriptor is valid, we are way ahead of the DMA | ||
537 | * engine, so just return an error condition. | ||
538 | */ | ||
539 | if (dp->dscr_cmd0 & DSCR_CMD0_V) { | ||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | /* Load up buffer address and byte count. | ||
544 | */ | ||
545 | dp->dscr_source0 = virt_to_phys(buf); | ||
546 | dp->dscr_cmd1 = nbytes; | ||
547 | dp->dscr_cmd0 |= DSCR_CMD0_V; /* Let it rip */ | ||
548 | ctp->chan_ptr->ddma_dbell = 0xffffffff; /* Make it go */ | ||
549 | |||
550 | /* Get next descriptor pointer. | ||
551 | */ | ||
552 | ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); | ||
553 | |||
554 | /* return something not zero. | ||
555 | */ | ||
556 | return nbytes; | ||
557 | } | ||
558 | |||
559 | /* Put a destination buffer into the DMA ring. | ||
560 | * This updates the destination pointer and byte count. Normally used | ||
561 | * to place an empty buffer into the ring for fifo to memory transfers. | ||
562 | */ | ||
563 | u32 | ||
564 | au1xxx_dbdma_put_dest(u32 chanid, void *buf, int nbytes) | ||
565 | { | ||
566 | chan_tab_t *ctp; | ||
567 | au1x_ddma_desc_t *dp; | ||
568 | |||
569 | /* I guess we could check this to be within the | ||
570 | * range of the table...... | ||
571 | */ | ||
572 | ctp = *((chan_tab_t **)chanid); | ||
573 | |||
574 | /* We should have multiple callers for a particular channel, | ||
575 | * an interrupt doesn't affect this pointer nor the descriptor, | ||
576 | * so no locking should be needed. | ||
577 | */ | ||
578 | dp = ctp->put_ptr; | ||
579 | |||
580 | /* If the descriptor is valid, we are way ahead of the DMA | ||
581 | * engine, so just return an error condition. | ||
582 | */ | ||
583 | if (dp->dscr_cmd0 & DSCR_CMD0_V) | ||
584 | return 0; | ||
585 | |||
586 | /* Load up buffer address and byte count. | ||
587 | */ | ||
588 | dp->dscr_dest0 = virt_to_phys(buf); | ||
589 | dp->dscr_cmd1 = nbytes; | ||
590 | dp->dscr_cmd0 |= DSCR_CMD0_V; /* Let it rip */ | ||
591 | |||
592 | /* Get next descriptor pointer. | ||
593 | */ | ||
594 | ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); | ||
595 | |||
596 | /* return something not zero. | ||
597 | */ | ||
598 | return nbytes; | ||
599 | } | ||
600 | |||
601 | /* Get a destination buffer into the DMA ring. | ||
602 | * Normally used to get a full buffer from the ring during fifo | ||
603 | * to memory transfers. This does not set the valid bit, you will | ||
604 | * have to put another destination buffer to keep the DMA going. | ||
605 | */ | ||
606 | u32 | ||
607 | au1xxx_dbdma_get_dest(u32 chanid, void **buf, int *nbytes) | ||
608 | { | ||
609 | chan_tab_t *ctp; | ||
610 | au1x_ddma_desc_t *dp; | ||
611 | u32 rv; | ||
612 | |||
613 | /* I guess we could check this to be within the | ||
614 | * range of the table...... | ||
615 | */ | ||
616 | ctp = *((chan_tab_t **)chanid); | ||
617 | |||
618 | /* We should have multiple callers for a particular channel, | ||
619 | * an interrupt doesn't affect this pointer nor the descriptor, | ||
620 | * so no locking should be needed. | ||
621 | */ | ||
622 | dp = ctp->get_ptr; | ||
623 | |||
624 | /* If the descriptor is valid, we are way ahead of the DMA | ||
625 | * engine, so just return an error condition. | ||
626 | */ | ||
627 | if (dp->dscr_cmd0 & DSCR_CMD0_V) | ||
628 | return 0; | ||
629 | |||
630 | /* Return buffer address and byte count. | ||
631 | */ | ||
632 | *buf = (void *)(phys_to_virt(dp->dscr_dest0)); | ||
633 | *nbytes = dp->dscr_cmd1; | ||
634 | rv = dp->dscr_stat; | ||
635 | |||
636 | /* Get next descriptor pointer. | ||
637 | */ | ||
638 | ctp->get_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); | ||
639 | |||
640 | /* return something not zero. | ||
641 | */ | ||
642 | return rv; | ||
643 | } | ||
644 | |||
645 | void | ||
646 | au1xxx_dbdma_stop(u32 chanid) | ||
647 | { | ||
648 | chan_tab_t *ctp; | ||
649 | volatile au1x_dma_chan_t *cp; | ||
650 | int halt_timeout = 0; | ||
651 | |||
652 | ctp = *((chan_tab_t **)chanid); | ||
653 | |||
654 | cp = ctp->chan_ptr; | ||
655 | cp->ddma_cfg &= ~DDMA_CFG_EN; /* Disable channel */ | ||
656 | au_sync(); | ||
657 | while (!(cp->ddma_stat & DDMA_STAT_H)) { | ||
658 | udelay(1); | ||
659 | halt_timeout++; | ||
660 | if (halt_timeout > 100) { | ||
661 | printk("warning: DMA channel won't halt\n"); | ||
662 | break; | ||
663 | } | ||
664 | } | ||
665 | /* clear current desc valid and doorbell */ | ||
666 | cp->ddma_stat |= (DDMA_STAT_DB | DDMA_STAT_V); | ||
667 | au_sync(); | ||
668 | } | ||
669 | |||
670 | /* Start using the current descriptor pointer. If the dbdma encounters | ||
671 | * a not valid descriptor, it will stop. In this case, we can just | ||
672 | * continue by adding a buffer to the list and starting again. | ||
673 | */ | ||
674 | void | ||
675 | au1xxx_dbdma_start(u32 chanid) | ||
676 | { | ||
677 | chan_tab_t *ctp; | ||
678 | volatile au1x_dma_chan_t *cp; | ||
679 | |||
680 | ctp = *((chan_tab_t **)chanid); | ||
681 | |||
682 | cp = ctp->chan_ptr; | ||
683 | cp->ddma_desptr = virt_to_phys(ctp->cur_ptr); | ||
684 | cp->ddma_cfg |= DDMA_CFG_EN; /* Enable channel */ | ||
685 | au_sync(); | ||
686 | cp->ddma_dbell = 0xffffffff; /* Make it go */ | ||
687 | au_sync(); | ||
688 | } | ||
689 | |||
690 | void | ||
691 | au1xxx_dbdma_reset(u32 chanid) | ||
692 | { | ||
693 | chan_tab_t *ctp; | ||
694 | au1x_ddma_desc_t *dp; | ||
695 | |||
696 | au1xxx_dbdma_stop(chanid); | ||
697 | |||
698 | ctp = *((chan_tab_t **)chanid); | ||
699 | ctp->get_ptr = ctp->put_ptr = ctp->cur_ptr = ctp->chan_desc_base; | ||
700 | |||
701 | /* Run through the descriptors and reset the valid indicator. | ||
702 | */ | ||
703 | dp = ctp->chan_desc_base; | ||
704 | |||
705 | do { | ||
706 | dp->dscr_cmd0 &= ~DSCR_CMD0_V; | ||
707 | dp = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); | ||
708 | } while (dp != ctp->chan_desc_base); | ||
709 | } | ||
710 | |||
711 | u32 | ||
712 | au1xxx_get_dma_residue(u32 chanid) | ||
713 | { | ||
714 | chan_tab_t *ctp; | ||
715 | volatile au1x_dma_chan_t *cp; | ||
716 | u32 rv; | ||
717 | |||
718 | ctp = *((chan_tab_t **)chanid); | ||
719 | cp = ctp->chan_ptr; | ||
720 | |||
721 | /* This is only valid if the channel is stopped. | ||
722 | */ | ||
723 | rv = cp->ddma_bytecnt; | ||
724 | au_sync(); | ||
725 | |||
726 | return rv; | ||
727 | } | ||
728 | |||
729 | void | ||
730 | au1xxx_dbdma_chan_free(u32 chanid) | ||
731 | { | ||
732 | chan_tab_t *ctp; | ||
733 | dbdev_tab_t *stp, *dtp; | ||
734 | |||
735 | ctp = *((chan_tab_t **)chanid); | ||
736 | stp = ctp->chan_src; | ||
737 | dtp = ctp->chan_dest; | ||
738 | |||
739 | au1xxx_dbdma_stop(chanid); | ||
740 | |||
741 | if (ctp->chan_desc_base != NULL) | ||
742 | kfree(ctp->chan_desc_base); | ||
743 | |||
744 | stp->dev_flags &= ~DEV_FLAGS_INUSE; | ||
745 | dtp->dev_flags &= ~DEV_FLAGS_INUSE; | ||
746 | chan_tab_ptr[ctp->chan_index] = NULL; | ||
747 | |||
748 | kfree(ctp); | ||
749 | } | ||
750 | |||
751 | static irqreturn_t | ||
752 | dbdma_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
753 | { | ||
754 | u32 intstat; | ||
755 | u32 chan_index; | ||
756 | chan_tab_t *ctp; | ||
757 | au1x_ddma_desc_t *dp; | ||
758 | volatile au1x_dma_chan_t *cp; | ||
759 | |||
760 | intstat = dbdma_gptr->ddma_intstat; | ||
761 | au_sync(); | ||
762 | chan_index = au_ffs(intstat) - 1; | ||
763 | |||
764 | ctp = chan_tab_ptr[chan_index]; | ||
765 | cp = ctp->chan_ptr; | ||
766 | dp = ctp->cur_ptr; | ||
767 | |||
768 | /* Reset interrupt. | ||
769 | */ | ||
770 | cp->ddma_irq = 0; | ||
771 | au_sync(); | ||
772 | |||
773 | if (ctp->chan_callback) | ||
774 | (ctp->chan_callback)(irq, ctp->chan_callparam, regs); | ||
775 | |||
776 | ctp->cur_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); | ||
777 | |||
778 | return IRQ_HANDLED; | ||
779 | } | ||
780 | |||
781 | static void | ||
782 | au1xxx_dbdma_init(void) | ||
783 | { | ||
784 | dbdma_gptr->ddma_config = 0; | ||
785 | dbdma_gptr->ddma_throttle = 0; | ||
786 | dbdma_gptr->ddma_inten = 0xffff; | ||
787 | au_sync(); | ||
788 | |||
789 | if (request_irq(AU1550_DDMA_INT, dbdma_interrupt, SA_INTERRUPT, | ||
790 | "Au1xxx dbdma", (void *)dbdma_gptr)) | ||
791 | printk("Can't get 1550 dbdma irq"); | ||
792 | } | ||
793 | |||
794 | void | ||
795 | au1xxx_dbdma_dump(u32 chanid) | ||
796 | { | ||
797 | chan_tab_t *ctp; | ||
798 | au1x_ddma_desc_t *dp; | ||
799 | dbdev_tab_t *stp, *dtp; | ||
800 | volatile au1x_dma_chan_t *cp; | ||
801 | |||
802 | ctp = *((chan_tab_t **)chanid); | ||
803 | stp = ctp->chan_src; | ||
804 | dtp = ctp->chan_dest; | ||
805 | cp = ctp->chan_ptr; | ||
806 | |||
807 | printk("Chan %x, stp %x (dev %d) dtp %x (dev %d) \n", | ||
808 | (u32)ctp, (u32)stp, stp - dbdev_tab, (u32)dtp, dtp - dbdev_tab); | ||
809 | printk("desc base %x, get %x, put %x, cur %x\n", | ||
810 | (u32)(ctp->chan_desc_base), (u32)(ctp->get_ptr), | ||
811 | (u32)(ctp->put_ptr), (u32)(ctp->cur_ptr)); | ||
812 | |||
813 | printk("dbdma chan %x\n", (u32)cp); | ||
814 | printk("cfg %08x, desptr %08x, statptr %08x\n", | ||
815 | cp->ddma_cfg, cp->ddma_desptr, cp->ddma_statptr); | ||
816 | printk("dbell %08x, irq %08x, stat %08x, bytecnt %08x\n", | ||
817 | cp->ddma_dbell, cp->ddma_irq, cp->ddma_stat, cp->ddma_bytecnt); | ||
818 | |||
819 | |||
820 | /* Run through the descriptors | ||
821 | */ | ||
822 | dp = ctp->chan_desc_base; | ||
823 | |||
824 | do { | ||
825 | printk("dp %08x, cmd0 %08x, cmd1 %08x\n", | ||
826 | (u32)dp, dp->dscr_cmd0, dp->dscr_cmd1); | ||
827 | printk("src0 %08x, src1 %08x, dest0 %08x\n", | ||
828 | dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0); | ||
829 | printk("dest1 %08x, stat %08x, nxtptr %08x\n", | ||
830 | dp->dscr_dest1, dp->dscr_stat, dp->dscr_nxtptr); | ||
831 | dp = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); | ||
832 | } while (dp != ctp->chan_desc_base); | ||
833 | } | ||
834 | |||
835 | #endif /* defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200) */ | ||
836 | |||
diff --git a/arch/mips/au1000/common/dbg_io.c b/arch/mips/au1000/common/dbg_io.c new file mode 100644 index 000000000000..7bc768e558db --- /dev/null +++ b/arch/mips/au1000/common/dbg_io.c | |||
@@ -0,0 +1,122 @@ | |||
1 | |||
2 | #include <linux/config.h> | ||
3 | #include <asm/io.h> | ||
4 | #include <asm/mach-au1x00/au1000.h> | ||
5 | |||
6 | #ifdef CONFIG_KGDB | ||
7 | |||
8 | /* | ||
9 | * FIXME the user should be able to select the | ||
10 | * uart to be used for debugging. | ||
11 | */ | ||
12 | #define DEBUG_BASE UART_DEBUG_BASE | ||
13 | /**/ | ||
14 | |||
15 | /* we need uint32 uint8 */ | ||
16 | /* #include "types.h" */ | ||
17 | typedef unsigned char uint8; | ||
18 | typedef unsigned int uint32; | ||
19 | |||
20 | #define UART16550_BAUD_2400 2400 | ||
21 | #define UART16550_BAUD_4800 4800 | ||
22 | #define UART16550_BAUD_9600 9600 | ||
23 | #define UART16550_BAUD_19200 19200 | ||
24 | #define UART16550_BAUD_38400 38400 | ||
25 | #define UART16550_BAUD_57600 57600 | ||
26 | #define UART16550_BAUD_115200 115200 | ||
27 | |||
28 | #define UART16550_PARITY_NONE 0 | ||
29 | #define UART16550_PARITY_ODD 0x08 | ||
30 | #define UART16550_PARITY_EVEN 0x18 | ||
31 | #define UART16550_PARITY_MARK 0x28 | ||
32 | #define UART16550_PARITY_SPACE 0x38 | ||
33 | |||
34 | #define UART16550_DATA_5BIT 0x0 | ||
35 | #define UART16550_DATA_6BIT 0x1 | ||
36 | #define UART16550_DATA_7BIT 0x2 | ||
37 | #define UART16550_DATA_8BIT 0x3 | ||
38 | |||
39 | #define UART16550_STOP_1BIT 0x0 | ||
40 | #define UART16550_STOP_2BIT 0x4 | ||
41 | |||
42 | |||
43 | #define UART_RX 0 /* Receive buffer */ | ||
44 | #define UART_TX 4 /* Transmit buffer */ | ||
45 | #define UART_IER 8 /* Interrupt Enable Register */ | ||
46 | #define UART_IIR 0xC /* Interrupt ID Register */ | ||
47 | #define UART_FCR 0x10 /* FIFO Control Register */ | ||
48 | #define UART_LCR 0x14 /* Line Control Register */ | ||
49 | #define UART_MCR 0x18 /* Modem Control Register */ | ||
50 | #define UART_LSR 0x1C /* Line Status Register */ | ||
51 | #define UART_MSR 0x20 /* Modem Status Register */ | ||
52 | #define UART_CLK 0x28 /* Baud Rat4e Clock Divider */ | ||
53 | #define UART_MOD_CNTRL 0x100 /* Module Control */ | ||
54 | |||
55 | /* memory-mapped read/write of the port */ | ||
56 | #define UART16550_READ(y) (au_readl(DEBUG_BASE + y) & 0xff) | ||
57 | #define UART16550_WRITE(y,z) (au_writel(z&0xff, DEBUG_BASE + y)) | ||
58 | |||
59 | extern unsigned long get_au1x00_uart_baud_base(void); | ||
60 | extern unsigned long cal_r4koff(void); | ||
61 | |||
62 | void debugInit(uint32 baud, uint8 data, uint8 parity, uint8 stop) | ||
63 | { | ||
64 | |||
65 | if (UART16550_READ(UART_MOD_CNTRL) != 0x3) { | ||
66 | UART16550_WRITE(UART_MOD_CNTRL, 3); | ||
67 | } | ||
68 | cal_r4koff(); | ||
69 | |||
70 | /* disable interrupts */ | ||
71 | UART16550_WRITE(UART_IER, 0); | ||
72 | |||
73 | /* set up baud rate */ | ||
74 | { | ||
75 | uint32 divisor; | ||
76 | |||
77 | /* set divisor */ | ||
78 | divisor = get_au1x00_uart_baud_base() / baud; | ||
79 | UART16550_WRITE(UART_CLK, divisor & 0xffff); | ||
80 | } | ||
81 | |||
82 | /* set data format */ | ||
83 | UART16550_WRITE(UART_LCR, (data | parity | stop)); | ||
84 | } | ||
85 | |||
86 | static int remoteDebugInitialized = 0; | ||
87 | |||
88 | uint8 getDebugChar(void) | ||
89 | { | ||
90 | if (!remoteDebugInitialized) { | ||
91 | remoteDebugInitialized = 1; | ||
92 | debugInit(UART16550_BAUD_115200, | ||
93 | UART16550_DATA_8BIT, | ||
94 | UART16550_PARITY_NONE, | ||
95 | UART16550_STOP_1BIT); | ||
96 | } | ||
97 | |||
98 | while((UART16550_READ(UART_LSR) & 0x1) == 0); | ||
99 | return UART16550_READ(UART_RX); | ||
100 | } | ||
101 | |||
102 | |||
103 | int putDebugChar(uint8 byte) | ||
104 | { | ||
105 | // int i; | ||
106 | |||
107 | if (!remoteDebugInitialized) { | ||
108 | remoteDebugInitialized = 1; | ||
109 | debugInit(UART16550_BAUD_115200, | ||
110 | UART16550_DATA_8BIT, | ||
111 | UART16550_PARITY_NONE, | ||
112 | UART16550_STOP_1BIT); | ||
113 | } | ||
114 | |||
115 | while ((UART16550_READ(UART_LSR)&0x40) == 0); | ||
116 | UART16550_WRITE(UART_TX, byte); | ||
117 | //for (i=0;i<0xfff;i++); | ||
118 | |||
119 | return 1; | ||
120 | } | ||
121 | |||
122 | #endif | ||
diff --git a/arch/mips/au1000/common/dma.c b/arch/mips/au1000/common/dma.c new file mode 100644 index 000000000000..372c33f1353d --- /dev/null +++ b/arch/mips/au1000/common/dma.c | |||
@@ -0,0 +1,243 @@ | |||
1 | /* | ||
2 | * | ||
3 | * BRIEF MODULE DESCRIPTION | ||
4 | * A DMA channel allocator for Au1000. API is modeled loosely off of | ||
5 | * linux/kernel/dma.c. | ||
6 | * | ||
7 | * Copyright 2000 MontaVista Software Inc. | ||
8 | * Author: MontaVista Software, Inc. | ||
9 | * stevel@mvista.com or source@mvista.com | ||
10 | * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org) | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify it | ||
13 | * under the terms of the GNU General Public License as published by the | ||
14 | * Free Software Foundation; either version 2 of the License, or (at your | ||
15 | * option) any later version. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
19 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
20 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
23 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
24 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
27 | * | ||
28 | * You should have received a copy of the GNU General Public License along | ||
29 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
30 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
31 | * | ||
32 | */ | ||
33 | #include <linux/config.h> | ||
34 | #include <linux/module.h> | ||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/errno.h> | ||
37 | #include <linux/sched.h> | ||
38 | #include <linux/spinlock.h> | ||
39 | #include <linux/string.h> | ||
40 | #include <linux/delay.h> | ||
41 | #include <linux/interrupt.h> | ||
42 | #include <linux/module.h> | ||
43 | #include <asm/system.h> | ||
44 | #include <asm/mach-au1x00/au1000.h> | ||
45 | #include <asm/mach-au1x00/au1000_dma.h> | ||
46 | |||
47 | #if defined(CONFIG_SOC_AU1000) || defined(CONFIG_SOC_AU1500) || defined(CONFIG_SOC_AU1100) | ||
48 | /* | ||
49 | * A note on resource allocation: | ||
50 | * | ||
51 | * All drivers needing DMA channels, should allocate and release them | ||
52 | * through the public routines `request_dma()' and `free_dma()'. | ||
53 | * | ||
54 | * In order to avoid problems, all processes should allocate resources in | ||
55 | * the same sequence and release them in the reverse order. | ||
56 | * | ||
57 | * So, when allocating DMAs and IRQs, first allocate the DMA, then the IRQ. | ||
58 | * When releasing them, first release the IRQ, then release the DMA. The | ||
59 | * main reason for this order is that, if you are requesting the DMA buffer | ||
60 | * done interrupt, you won't know the irq number until the DMA channel is | ||
61 | * returned from request_dma. | ||
62 | */ | ||
63 | |||
64 | |||
65 | DEFINE_SPINLOCK(au1000_dma_spin_lock); | ||
66 | |||
67 | struct dma_chan au1000_dma_table[NUM_AU1000_DMA_CHANNELS] = { | ||
68 | {.dev_id = -1,}, | ||
69 | {.dev_id = -1,}, | ||
70 | {.dev_id = -1,}, | ||
71 | {.dev_id = -1,}, | ||
72 | {.dev_id = -1,}, | ||
73 | {.dev_id = -1,}, | ||
74 | {.dev_id = -1,}, | ||
75 | {.dev_id = -1,} | ||
76 | }; | ||
77 | EXPORT_SYMBOL(au1000_dma_table); | ||
78 | |||
79 | // Device FIFO addresses and default DMA modes | ||
80 | static const struct dma_dev { | ||
81 | unsigned int fifo_addr; | ||
82 | unsigned int dma_mode; | ||
83 | } dma_dev_table[DMA_NUM_DEV] = { | ||
84 | {UART0_ADDR + UART_TX, 0}, | ||
85 | {UART0_ADDR + UART_RX, 0}, | ||
86 | {0, 0}, | ||
87 | {0, 0}, | ||
88 | {AC97C_DATA, DMA_DW16 }, // coherent | ||
89 | {AC97C_DATA, DMA_DR | DMA_DW16 }, // coherent | ||
90 | {UART3_ADDR + UART_TX, DMA_DW8 | DMA_NC}, | ||
91 | {UART3_ADDR + UART_RX, DMA_DR | DMA_DW8 | DMA_NC}, | ||
92 | {USBD_EP0RD, DMA_DR | DMA_DW8 | DMA_NC}, | ||
93 | {USBD_EP0WR, DMA_DW8 | DMA_NC}, | ||
94 | {USBD_EP2WR, DMA_DW8 | DMA_NC}, | ||
95 | {USBD_EP3WR, DMA_DW8 | DMA_NC}, | ||
96 | {USBD_EP4RD, DMA_DR | DMA_DW8 | DMA_NC}, | ||
97 | {USBD_EP5RD, DMA_DR | DMA_DW8 | DMA_NC}, | ||
98 | {I2S_DATA, DMA_DW32 | DMA_NC}, | ||
99 | {I2S_DATA, DMA_DR | DMA_DW32 | DMA_NC} | ||
100 | }; | ||
101 | |||
102 | int au1000_dma_read_proc(char *buf, char **start, off_t fpos, | ||
103 | int length, int *eof, void *data) | ||
104 | { | ||
105 | int i, len = 0; | ||
106 | struct dma_chan *chan; | ||
107 | |||
108 | for (i = 0; i < NUM_AU1000_DMA_CHANNELS; i++) { | ||
109 | if ((chan = get_dma_chan(i)) != NULL) { | ||
110 | len += sprintf(buf + len, "%2d: %s\n", | ||
111 | i, chan->dev_str); | ||
112 | } | ||
113 | } | ||
114 | |||
115 | if (fpos >= len) { | ||
116 | *start = buf; | ||
117 | *eof = 1; | ||
118 | return 0; | ||
119 | } | ||
120 | *start = buf + fpos; | ||
121 | if ((len -= fpos) > length) | ||
122 | return length; | ||
123 | *eof = 1; | ||
124 | return len; | ||
125 | } | ||
126 | |||
127 | // Device FIFO addresses and default DMA modes - 2nd bank | ||
128 | static const struct dma_dev dma_dev_table_bank2[DMA_NUM_DEV_BANK2] = { | ||
129 | {SD0_XMIT_FIFO, DMA_DS | DMA_DW8}, // coherent | ||
130 | {SD0_RECV_FIFO, DMA_DS | DMA_DR | DMA_DW8}, // coherent | ||
131 | {SD1_XMIT_FIFO, DMA_DS | DMA_DW8}, // coherent | ||
132 | {SD1_RECV_FIFO, DMA_DS | DMA_DR | DMA_DW8} // coherent | ||
133 | }; | ||
134 | |||
135 | void dump_au1000_dma_channel(unsigned int dmanr) | ||
136 | { | ||
137 | struct dma_chan *chan; | ||
138 | |||
139 | if (dmanr >= NUM_AU1000_DMA_CHANNELS) | ||
140 | return; | ||
141 | chan = &au1000_dma_table[dmanr]; | ||
142 | |||
143 | printk(KERN_INFO "Au1000 DMA%d Register Dump:\n", dmanr); | ||
144 | printk(KERN_INFO " mode = 0x%08x\n", | ||
145 | au_readl(chan->io + DMA_MODE_SET)); | ||
146 | printk(KERN_INFO " addr = 0x%08x\n", | ||
147 | au_readl(chan->io + DMA_PERIPHERAL_ADDR)); | ||
148 | printk(KERN_INFO " start0 = 0x%08x\n", | ||
149 | au_readl(chan->io + DMA_BUFFER0_START)); | ||
150 | printk(KERN_INFO " start1 = 0x%08x\n", | ||
151 | au_readl(chan->io + DMA_BUFFER1_START)); | ||
152 | printk(KERN_INFO " count0 = 0x%08x\n", | ||
153 | au_readl(chan->io + DMA_BUFFER0_COUNT)); | ||
154 | printk(KERN_INFO " count1 = 0x%08x\n", | ||
155 | au_readl(chan->io + DMA_BUFFER1_COUNT)); | ||
156 | } | ||
157 | |||
158 | |||
159 | /* | ||
160 | * Finds a free channel, and binds the requested device to it. | ||
161 | * Returns the allocated channel number, or negative on error. | ||
162 | * Requests the DMA done IRQ if irqhandler != NULL. | ||
163 | */ | ||
164 | int request_au1000_dma(int dev_id, const char *dev_str, | ||
165 | irqreturn_t (*irqhandler)(int, void *, struct pt_regs *), | ||
166 | unsigned long irqflags, | ||
167 | void *irq_dev_id) | ||
168 | { | ||
169 | struct dma_chan *chan; | ||
170 | const struct dma_dev *dev; | ||
171 | int i, ret; | ||
172 | |||
173 | #if defined(CONFIG_SOC_AU1100) | ||
174 | if (dev_id < 0 || dev_id >= (DMA_NUM_DEV + DMA_NUM_DEV_BANK2)) | ||
175 | return -EINVAL; | ||
176 | #else | ||
177 | if (dev_id < 0 || dev_id >= DMA_NUM_DEV) | ||
178 | return -EINVAL; | ||
179 | #endif | ||
180 | |||
181 | for (i = 0; i < NUM_AU1000_DMA_CHANNELS; i++) { | ||
182 | if (au1000_dma_table[i].dev_id < 0) | ||
183 | break; | ||
184 | } | ||
185 | if (i == NUM_AU1000_DMA_CHANNELS) | ||
186 | return -ENODEV; | ||
187 | |||
188 | chan = &au1000_dma_table[i]; | ||
189 | |||
190 | if (dev_id >= DMA_NUM_DEV) { | ||
191 | dev_id -= DMA_NUM_DEV; | ||
192 | dev = &dma_dev_table_bank2[dev_id]; | ||
193 | } else { | ||
194 | dev = &dma_dev_table[dev_id]; | ||
195 | } | ||
196 | |||
197 | if (irqhandler) { | ||
198 | chan->irq = AU1000_DMA_INT_BASE + i; | ||
199 | chan->irq_dev = irq_dev_id; | ||
200 | if ((ret = request_irq(chan->irq, irqhandler, irqflags, | ||
201 | dev_str, chan->irq_dev))) { | ||
202 | chan->irq = 0; | ||
203 | chan->irq_dev = NULL; | ||
204 | return ret; | ||
205 | } | ||
206 | } else { | ||
207 | chan->irq = 0; | ||
208 | chan->irq_dev = NULL; | ||
209 | } | ||
210 | |||
211 | // fill it in | ||
212 | chan->io = DMA_CHANNEL_BASE + i * DMA_CHANNEL_LEN; | ||
213 | chan->dev_id = dev_id; | ||
214 | chan->dev_str = dev_str; | ||
215 | chan->fifo_addr = dev->fifo_addr; | ||
216 | chan->mode = dev->dma_mode; | ||
217 | |||
218 | /* initialize the channel before returning */ | ||
219 | init_dma(i); | ||
220 | |||
221 | return i; | ||
222 | } | ||
223 | EXPORT_SYMBOL(request_au1000_dma); | ||
224 | |||
225 | void free_au1000_dma(unsigned int dmanr) | ||
226 | { | ||
227 | struct dma_chan *chan = get_dma_chan(dmanr); | ||
228 | if (!chan) { | ||
229 | printk("Trying to free DMA%d\n", dmanr); | ||
230 | return; | ||
231 | } | ||
232 | |||
233 | disable_dma(dmanr); | ||
234 | if (chan->irq) | ||
235 | free_irq(chan->irq, chan->irq_dev); | ||
236 | |||
237 | chan->irq = 0; | ||
238 | chan->irq_dev = NULL; | ||
239 | chan->dev_id = -1; | ||
240 | } | ||
241 | EXPORT_SYMBOL(free_au1000_dma); | ||
242 | |||
243 | #endif // AU1000 AU1500 AU1100 | ||
diff --git a/arch/mips/au1000/common/int-handler.S b/arch/mips/au1000/common/int-handler.S new file mode 100644 index 000000000000..1c4ca883321e --- /dev/null +++ b/arch/mips/au1000/common/int-handler.S | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * Copyright 2001 MontaVista Software Inc. | ||
3 | * Author: ppopov@mvista.com | ||
4 | * | ||
5 | * Interrupt dispatcher for Au1000 boards. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | */ | ||
12 | #include <asm/asm.h> | ||
13 | #include <asm/mipsregs.h> | ||
14 | #include <asm/addrspace.h> | ||
15 | #include <asm/regdef.h> | ||
16 | #include <asm/stackframe.h> | ||
17 | |||
18 | .text | ||
19 | .set macro | ||
20 | .set noat | ||
21 | .align 5 | ||
22 | |||
23 | NESTED(au1000_IRQ, PT_SIZE, sp) | ||
24 | SAVE_ALL | ||
25 | CLI # Important: mark KERNEL mode ! | ||
26 | |||
27 | mfc0 t0,CP0_CAUSE # get pending interrupts | ||
28 | mfc0 t1,CP0_STATUS # get enabled interrupts | ||
29 | and t0,t1 # isolate allowed ones | ||
30 | |||
31 | andi t0,0xff00 # isolate pending bits | ||
32 | beqz t0, 3f # spurious interrupt | ||
33 | |||
34 | andi a0, t0, CAUSEF_IP7 | ||
35 | beq a0, zero, 1f | ||
36 | move a0, sp | ||
37 | jal mips_timer_interrupt | ||
38 | j ret_from_irq | ||
39 | |||
40 | 1: | ||
41 | andi a0, t0, CAUSEF_IP2 # Interrupt Controller 0, Request 0 | ||
42 | beq a0, zero, 2f | ||
43 | move a0,sp | ||
44 | jal intc0_req0_irqdispatch | ||
45 | j ret_from_irq | ||
46 | 2: | ||
47 | andi a0, t0, CAUSEF_IP3 # Interrupt Controller 0, Request 1 | ||
48 | beq a0, zero, 3f | ||
49 | move a0,sp | ||
50 | jal intc0_req1_irqdispatch | ||
51 | j ret_from_irq | ||
52 | 3: | ||
53 | andi a0, t0, CAUSEF_IP4 # Interrupt Controller 1, Request 0 | ||
54 | beq a0, zero, 4f | ||
55 | move a0,sp | ||
56 | jal intc1_req0_irqdispatch | ||
57 | j ret_from_irq | ||
58 | 4: | ||
59 | andi a0, t0, CAUSEF_IP5 # Interrupt Controller 1, Request 1 | ||
60 | beq a0, zero, 5f | ||
61 | move a0, sp | ||
62 | jal intc1_req1_irqdispatch | ||
63 | j ret_from_irq | ||
64 | |||
65 | 5: | ||
66 | move a0, sp | ||
67 | j spurious_interrupt | ||
68 | END(au1000_IRQ) | ||
diff --git a/arch/mips/au1000/common/irq.c b/arch/mips/au1000/common/irq.c new file mode 100644 index 000000000000..d1eb5a4a9a19 --- /dev/null +++ b/arch/mips/au1000/common/irq.c | |||
@@ -0,0 +1,654 @@ | |||
1 | /* | ||
2 | * BRIEF MODULE DESCRIPTION | ||
3 | * Au1000 interrupt routines. | ||
4 | * | ||
5 | * Copyright 2001 MontaVista Software Inc. | ||
6 | * Author: MontaVista Software, Inc. | ||
7 | * ppopov@mvista.com or source@mvista.com | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License as published by the | ||
11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
12 | * option) any later version. | ||
13 | * | ||
14 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
15 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
16 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
17 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
20 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
21 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
24 | * | ||
25 | * You should have received a copy of the GNU General Public License along | ||
26 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
27 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
28 | */ | ||
29 | #include <linux/config.h> | ||
30 | #include <linux/errno.h> | ||
31 | #include <linux/init.h> | ||
32 | #include <linux/irq.h> | ||
33 | #include <linux/kernel_stat.h> | ||
34 | #include <linux/module.h> | ||
35 | #include <linux/signal.h> | ||
36 | #include <linux/sched.h> | ||
37 | #include <linux/types.h> | ||
38 | #include <linux/interrupt.h> | ||
39 | #include <linux/ioport.h> | ||
40 | #include <linux/timex.h> | ||
41 | #include <linux/slab.h> | ||
42 | #include <linux/random.h> | ||
43 | #include <linux/delay.h> | ||
44 | #include <linux/bitops.h> | ||
45 | |||
46 | #include <asm/bootinfo.h> | ||
47 | #include <asm/io.h> | ||
48 | #include <asm/mipsregs.h> | ||
49 | #include <asm/system.h> | ||
50 | #include <asm/mach-au1x00/au1000.h> | ||
51 | #ifdef CONFIG_MIPS_PB1000 | ||
52 | #include <asm/mach-pb1x00/pb1000.h> | ||
53 | #endif | ||
54 | |||
55 | #undef DEBUG_IRQ | ||
56 | #ifdef DEBUG_IRQ | ||
57 | /* note: prints function name for you */ | ||
58 | #define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args) | ||
59 | #else | ||
60 | #define DPRINTK(fmt, args...) | ||
61 | #endif | ||
62 | |||
63 | #define EXT_INTC0_REQ0 2 /* IP 2 */ | ||
64 | #define EXT_INTC0_REQ1 3 /* IP 3 */ | ||
65 | #define EXT_INTC1_REQ0 4 /* IP 4 */ | ||
66 | #define EXT_INTC1_REQ1 5 /* IP 5 */ | ||
67 | #define MIPS_TIMER_IP 7 /* IP 7 */ | ||
68 | |||
69 | extern asmlinkage void au1000_IRQ(void); | ||
70 | extern void set_debug_traps(void); | ||
71 | extern irq_cpustat_t irq_stat [NR_CPUS]; | ||
72 | |||
73 | static void setup_local_irq(unsigned int irq, int type, int int_req); | ||
74 | static unsigned int startup_irq(unsigned int irq); | ||
75 | static void end_irq(unsigned int irq_nr); | ||
76 | static inline void mask_and_ack_level_irq(unsigned int irq_nr); | ||
77 | static inline void mask_and_ack_rise_edge_irq(unsigned int irq_nr); | ||
78 | static inline void mask_and_ack_fall_edge_irq(unsigned int irq_nr); | ||
79 | static inline void mask_and_ack_either_edge_irq(unsigned int irq_nr); | ||
80 | inline void local_enable_irq(unsigned int irq_nr); | ||
81 | inline void local_disable_irq(unsigned int irq_nr); | ||
82 | |||
83 | void (*board_init_irq)(void); | ||
84 | |||
85 | #ifdef CONFIG_PM | ||
86 | extern void counter0_irq(int irq, void *dev_id, struct pt_regs *regs); | ||
87 | #endif | ||
88 | |||
89 | static DEFINE_SPINLOCK(irq_lock); | ||
90 | |||
91 | |||
92 | static unsigned int startup_irq(unsigned int irq_nr) | ||
93 | { | ||
94 | local_enable_irq(irq_nr); | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | |||
99 | static void shutdown_irq(unsigned int irq_nr) | ||
100 | { | ||
101 | local_disable_irq(irq_nr); | ||
102 | return; | ||
103 | } | ||
104 | |||
105 | |||
106 | inline void local_enable_irq(unsigned int irq_nr) | ||
107 | { | ||
108 | if (irq_nr > AU1000_LAST_INTC0_INT) { | ||
109 | au_writel(1<<(irq_nr-32), IC1_MASKSET); | ||
110 | au_writel(1<<(irq_nr-32), IC1_WAKESET); | ||
111 | } | ||
112 | else { | ||
113 | au_writel(1<<irq_nr, IC0_MASKSET); | ||
114 | au_writel(1<<irq_nr, IC0_WAKESET); | ||
115 | } | ||
116 | au_sync(); | ||
117 | } | ||
118 | |||
119 | |||
120 | inline void local_disable_irq(unsigned int irq_nr) | ||
121 | { | ||
122 | if (irq_nr > AU1000_LAST_INTC0_INT) { | ||
123 | au_writel(1<<(irq_nr-32), IC1_MASKCLR); | ||
124 | au_writel(1<<(irq_nr-32), IC1_WAKECLR); | ||
125 | } | ||
126 | else { | ||
127 | au_writel(1<<irq_nr, IC0_MASKCLR); | ||
128 | au_writel(1<<irq_nr, IC0_WAKECLR); | ||
129 | } | ||
130 | au_sync(); | ||
131 | } | ||
132 | |||
133 | |||
134 | static inline void mask_and_ack_rise_edge_irq(unsigned int irq_nr) | ||
135 | { | ||
136 | if (irq_nr > AU1000_LAST_INTC0_INT) { | ||
137 | au_writel(1<<(irq_nr-32), IC1_RISINGCLR); | ||
138 | au_writel(1<<(irq_nr-32), IC1_MASKCLR); | ||
139 | } | ||
140 | else { | ||
141 | au_writel(1<<irq_nr, IC0_RISINGCLR); | ||
142 | au_writel(1<<irq_nr, IC0_MASKCLR); | ||
143 | } | ||
144 | au_sync(); | ||
145 | } | ||
146 | |||
147 | |||
148 | static inline void mask_and_ack_fall_edge_irq(unsigned int irq_nr) | ||
149 | { | ||
150 | if (irq_nr > AU1000_LAST_INTC0_INT) { | ||
151 | au_writel(1<<(irq_nr-32), IC1_FALLINGCLR); | ||
152 | au_writel(1<<(irq_nr-32), IC1_MASKCLR); | ||
153 | } | ||
154 | else { | ||
155 | au_writel(1<<irq_nr, IC0_FALLINGCLR); | ||
156 | au_writel(1<<irq_nr, IC0_MASKCLR); | ||
157 | } | ||
158 | au_sync(); | ||
159 | } | ||
160 | |||
161 | |||
162 | static inline void mask_and_ack_either_edge_irq(unsigned int irq_nr) | ||
163 | { | ||
164 | /* This may assume that we don't get interrupts from | ||
165 | * both edges at once, or if we do, that we don't care. | ||
166 | */ | ||
167 | if (irq_nr > AU1000_LAST_INTC0_INT) { | ||
168 | au_writel(1<<(irq_nr-32), IC1_FALLINGCLR); | ||
169 | au_writel(1<<(irq_nr-32), IC1_RISINGCLR); | ||
170 | au_writel(1<<(irq_nr-32), IC1_MASKCLR); | ||
171 | } | ||
172 | else { | ||
173 | au_writel(1<<irq_nr, IC0_FALLINGCLR); | ||
174 | au_writel(1<<irq_nr, IC0_RISINGCLR); | ||
175 | au_writel(1<<irq_nr, IC0_MASKCLR); | ||
176 | } | ||
177 | au_sync(); | ||
178 | } | ||
179 | |||
180 | |||
181 | static inline void mask_and_ack_level_irq(unsigned int irq_nr) | ||
182 | { | ||
183 | |||
184 | local_disable_irq(irq_nr); | ||
185 | au_sync(); | ||
186 | #if defined(CONFIG_MIPS_PB1000) | ||
187 | if (irq_nr == AU1000_GPIO_15) { | ||
188 | au_writel(0x8000, PB1000_MDR); /* ack int */ | ||
189 | au_sync(); | ||
190 | } | ||
191 | #endif | ||
192 | return; | ||
193 | } | ||
194 | |||
195 | |||
196 | static void end_irq(unsigned int irq_nr) | ||
197 | { | ||
198 | if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { | ||
199 | local_enable_irq(irq_nr); | ||
200 | } | ||
201 | #if defined(CONFIG_MIPS_PB1000) | ||
202 | if (irq_nr == AU1000_GPIO_15) { | ||
203 | au_writel(0x4000, PB1000_MDR); /* enable int */ | ||
204 | au_sync(); | ||
205 | } | ||
206 | #endif | ||
207 | } | ||
208 | |||
209 | unsigned long save_local_and_disable(int controller) | ||
210 | { | ||
211 | int i; | ||
212 | unsigned long flags, mask; | ||
213 | |||
214 | spin_lock_irqsave(&irq_lock, flags); | ||
215 | if (controller) { | ||
216 | mask = au_readl(IC1_MASKSET); | ||
217 | for (i=32; i<64; i++) { | ||
218 | local_disable_irq(i); | ||
219 | } | ||
220 | } | ||
221 | else { | ||
222 | mask = au_readl(IC0_MASKSET); | ||
223 | for (i=0; i<32; i++) { | ||
224 | local_disable_irq(i); | ||
225 | } | ||
226 | } | ||
227 | spin_unlock_irqrestore(&irq_lock, flags); | ||
228 | |||
229 | return mask; | ||
230 | } | ||
231 | |||
232 | void restore_local_and_enable(int controller, unsigned long mask) | ||
233 | { | ||
234 | int i; | ||
235 | unsigned long flags, new_mask; | ||
236 | |||
237 | spin_lock_irqsave(&irq_lock, flags); | ||
238 | for (i=0; i<32; i++) { | ||
239 | if (mask & (1<<i)) { | ||
240 | if (controller) | ||
241 | local_enable_irq(i+32); | ||
242 | else | ||
243 | local_enable_irq(i); | ||
244 | } | ||
245 | } | ||
246 | if (controller) | ||
247 | new_mask = au_readl(IC1_MASKSET); | ||
248 | else | ||
249 | new_mask = au_readl(IC0_MASKSET); | ||
250 | |||
251 | spin_unlock_irqrestore(&irq_lock, flags); | ||
252 | } | ||
253 | |||
254 | |||
255 | static struct hw_interrupt_type rise_edge_irq_type = { | ||
256 | "Au1000 Rise Edge", | ||
257 | startup_irq, | ||
258 | shutdown_irq, | ||
259 | local_enable_irq, | ||
260 | local_disable_irq, | ||
261 | mask_and_ack_rise_edge_irq, | ||
262 | end_irq, | ||
263 | NULL | ||
264 | }; | ||
265 | |||
266 | static struct hw_interrupt_type fall_edge_irq_type = { | ||
267 | "Au1000 Fall Edge", | ||
268 | startup_irq, | ||
269 | shutdown_irq, | ||
270 | local_enable_irq, | ||
271 | local_disable_irq, | ||
272 | mask_and_ack_fall_edge_irq, | ||
273 | end_irq, | ||
274 | NULL | ||
275 | }; | ||
276 | |||
277 | static struct hw_interrupt_type either_edge_irq_type = { | ||
278 | "Au1000 Rise or Fall Edge", | ||
279 | startup_irq, | ||
280 | shutdown_irq, | ||
281 | local_enable_irq, | ||
282 | local_disable_irq, | ||
283 | mask_and_ack_either_edge_irq, | ||
284 | end_irq, | ||
285 | NULL | ||
286 | }; | ||
287 | |||
288 | static struct hw_interrupt_type level_irq_type = { | ||
289 | "Au1000 Level", | ||
290 | startup_irq, | ||
291 | shutdown_irq, | ||
292 | local_enable_irq, | ||
293 | local_disable_irq, | ||
294 | mask_and_ack_level_irq, | ||
295 | end_irq, | ||
296 | NULL | ||
297 | }; | ||
298 | |||
299 | #ifdef CONFIG_PM | ||
300 | void startup_match20_interrupt(void) | ||
301 | { | ||
302 | local_enable_irq(AU1000_TOY_MATCH2_INT); | ||
303 | } | ||
304 | #endif | ||
305 | |||
306 | static void setup_local_irq(unsigned int irq_nr, int type, int int_req) | ||
307 | { | ||
308 | if (irq_nr > AU1000_MAX_INTR) return; | ||
309 | /* Config2[n], Config1[n], Config0[n] */ | ||
310 | if (irq_nr > AU1000_LAST_INTC0_INT) { | ||
311 | switch (type) { | ||
312 | case INTC_INT_RISE_EDGE: /* 0:0:1 */ | ||
313 | au_writel(1<<(irq_nr-32), IC1_CFG2CLR); | ||
314 | au_writel(1<<(irq_nr-32), IC1_CFG1CLR); | ||
315 | au_writel(1<<(irq_nr-32), IC1_CFG0SET); | ||
316 | irq_desc[irq_nr].handler = &rise_edge_irq_type; | ||
317 | break; | ||
318 | case INTC_INT_FALL_EDGE: /* 0:1:0 */ | ||
319 | au_writel(1<<(irq_nr-32), IC1_CFG2CLR); | ||
320 | au_writel(1<<(irq_nr-32), IC1_CFG1SET); | ||
321 | au_writel(1<<(irq_nr-32), IC1_CFG0CLR); | ||
322 | irq_desc[irq_nr].handler = &fall_edge_irq_type; | ||
323 | break; | ||
324 | case INTC_INT_RISE_AND_FALL_EDGE: /* 0:1:1 */ | ||
325 | au_writel(1<<(irq_nr-32), IC1_CFG2CLR); | ||
326 | au_writel(1<<(irq_nr-32), IC1_CFG1SET); | ||
327 | au_writel(1<<(irq_nr-32), IC1_CFG0SET); | ||
328 | irq_desc[irq_nr].handler = &either_edge_irq_type; | ||
329 | break; | ||
330 | case INTC_INT_HIGH_LEVEL: /* 1:0:1 */ | ||
331 | au_writel(1<<(irq_nr-32), IC1_CFG2SET); | ||
332 | au_writel(1<<(irq_nr-32), IC1_CFG1CLR); | ||
333 | au_writel(1<<(irq_nr-32), IC1_CFG0SET); | ||
334 | irq_desc[irq_nr].handler = &level_irq_type; | ||
335 | break; | ||
336 | case INTC_INT_LOW_LEVEL: /* 1:1:0 */ | ||
337 | au_writel(1<<(irq_nr-32), IC1_CFG2SET); | ||
338 | au_writel(1<<(irq_nr-32), IC1_CFG1SET); | ||
339 | au_writel(1<<(irq_nr-32), IC1_CFG0CLR); | ||
340 | irq_desc[irq_nr].handler = &level_irq_type; | ||
341 | break; | ||
342 | case INTC_INT_DISABLED: /* 0:0:0 */ | ||
343 | au_writel(1<<(irq_nr-32), IC1_CFG0CLR); | ||
344 | au_writel(1<<(irq_nr-32), IC1_CFG1CLR); | ||
345 | au_writel(1<<(irq_nr-32), IC1_CFG2CLR); | ||
346 | break; | ||
347 | default: /* disable the interrupt */ | ||
348 | printk("unexpected int type %d (irq %d)\n", type, irq_nr); | ||
349 | au_writel(1<<(irq_nr-32), IC1_CFG0CLR); | ||
350 | au_writel(1<<(irq_nr-32), IC1_CFG1CLR); | ||
351 | au_writel(1<<(irq_nr-32), IC1_CFG2CLR); | ||
352 | return; | ||
353 | } | ||
354 | if (int_req) /* assign to interrupt request 1 */ | ||
355 | au_writel(1<<(irq_nr-32), IC1_ASSIGNCLR); | ||
356 | else /* assign to interrupt request 0 */ | ||
357 | au_writel(1<<(irq_nr-32), IC1_ASSIGNSET); | ||
358 | au_writel(1<<(irq_nr-32), IC1_SRCSET); | ||
359 | au_writel(1<<(irq_nr-32), IC1_MASKCLR); | ||
360 | au_writel(1<<(irq_nr-32), IC1_WAKECLR); | ||
361 | } | ||
362 | else { | ||
363 | switch (type) { | ||
364 | case INTC_INT_RISE_EDGE: /* 0:0:1 */ | ||
365 | au_writel(1<<irq_nr, IC0_CFG2CLR); | ||
366 | au_writel(1<<irq_nr, IC0_CFG1CLR); | ||
367 | au_writel(1<<irq_nr, IC0_CFG0SET); | ||
368 | irq_desc[irq_nr].handler = &rise_edge_irq_type; | ||
369 | break; | ||
370 | case INTC_INT_FALL_EDGE: /* 0:1:0 */ | ||
371 | au_writel(1<<irq_nr, IC0_CFG2CLR); | ||
372 | au_writel(1<<irq_nr, IC0_CFG1SET); | ||
373 | au_writel(1<<irq_nr, IC0_CFG0CLR); | ||
374 | irq_desc[irq_nr].handler = &fall_edge_irq_type; | ||
375 | break; | ||
376 | case INTC_INT_RISE_AND_FALL_EDGE: /* 0:1:1 */ | ||
377 | au_writel(1<<irq_nr, IC0_CFG2CLR); | ||
378 | au_writel(1<<irq_nr, IC0_CFG1SET); | ||
379 | au_writel(1<<irq_nr, IC0_CFG0SET); | ||
380 | irq_desc[irq_nr].handler = &either_edge_irq_type; | ||
381 | break; | ||
382 | case INTC_INT_HIGH_LEVEL: /* 1:0:1 */ | ||
383 | au_writel(1<<irq_nr, IC0_CFG2SET); | ||
384 | au_writel(1<<irq_nr, IC0_CFG1CLR); | ||
385 | au_writel(1<<irq_nr, IC0_CFG0SET); | ||
386 | irq_desc[irq_nr].handler = &level_irq_type; | ||
387 | break; | ||
388 | case INTC_INT_LOW_LEVEL: /* 1:1:0 */ | ||
389 | au_writel(1<<irq_nr, IC0_CFG2SET); | ||
390 | au_writel(1<<irq_nr, IC0_CFG1SET); | ||
391 | au_writel(1<<irq_nr, IC0_CFG0CLR); | ||
392 | irq_desc[irq_nr].handler = &level_irq_type; | ||
393 | break; | ||
394 | case INTC_INT_DISABLED: /* 0:0:0 */ | ||
395 | au_writel(1<<irq_nr, IC0_CFG0CLR); | ||
396 | au_writel(1<<irq_nr, IC0_CFG1CLR); | ||
397 | au_writel(1<<irq_nr, IC0_CFG2CLR); | ||
398 | break; | ||
399 | default: /* disable the interrupt */ | ||
400 | printk("unexpected int type %d (irq %d)\n", type, irq_nr); | ||
401 | au_writel(1<<irq_nr, IC0_CFG0CLR); | ||
402 | au_writel(1<<irq_nr, IC0_CFG1CLR); | ||
403 | au_writel(1<<irq_nr, IC0_CFG2CLR); | ||
404 | return; | ||
405 | } | ||
406 | if (int_req) /* assign to interrupt request 1 */ | ||
407 | au_writel(1<<irq_nr, IC0_ASSIGNCLR); | ||
408 | else /* assign to interrupt request 0 */ | ||
409 | au_writel(1<<irq_nr, IC0_ASSIGNSET); | ||
410 | au_writel(1<<irq_nr, IC0_SRCSET); | ||
411 | au_writel(1<<irq_nr, IC0_MASKCLR); | ||
412 | au_writel(1<<irq_nr, IC0_WAKECLR); | ||
413 | } | ||
414 | au_sync(); | ||
415 | } | ||
416 | |||
417 | |||
418 | void __init arch_init_irq(void) | ||
419 | { | ||
420 | int i; | ||
421 | unsigned long cp0_status; | ||
422 | au1xxx_irq_map_t *imp; | ||
423 | extern au1xxx_irq_map_t au1xxx_irq_map[]; | ||
424 | extern au1xxx_irq_map_t au1xxx_ic0_map[]; | ||
425 | extern int au1xxx_nr_irqs; | ||
426 | extern int au1xxx_ic0_nr_irqs; | ||
427 | |||
428 | cp0_status = read_c0_status(); | ||
429 | memset(irq_desc, 0, sizeof(irq_desc)); | ||
430 | set_except_vector(0, au1000_IRQ); | ||
431 | |||
432 | /* Initialize interrupt controllers to a safe state. | ||
433 | */ | ||
434 | au_writel(0xffffffff, IC0_CFG0CLR); | ||
435 | au_writel(0xffffffff, IC0_CFG1CLR); | ||
436 | au_writel(0xffffffff, IC0_CFG2CLR); | ||
437 | au_writel(0xffffffff, IC0_MASKCLR); | ||
438 | au_writel(0xffffffff, IC0_ASSIGNSET); | ||
439 | au_writel(0xffffffff, IC0_WAKECLR); | ||
440 | au_writel(0xffffffff, IC0_SRCSET); | ||
441 | au_writel(0xffffffff, IC0_FALLINGCLR); | ||
442 | au_writel(0xffffffff, IC0_RISINGCLR); | ||
443 | au_writel(0x00000000, IC0_TESTBIT); | ||
444 | |||
445 | au_writel(0xffffffff, IC1_CFG0CLR); | ||
446 | au_writel(0xffffffff, IC1_CFG1CLR); | ||
447 | au_writel(0xffffffff, IC1_CFG2CLR); | ||
448 | au_writel(0xffffffff, IC1_MASKCLR); | ||
449 | au_writel(0xffffffff, IC1_ASSIGNSET); | ||
450 | au_writel(0xffffffff, IC1_WAKECLR); | ||
451 | au_writel(0xffffffff, IC1_SRCSET); | ||
452 | au_writel(0xffffffff, IC1_FALLINGCLR); | ||
453 | au_writel(0xffffffff, IC1_RISINGCLR); | ||
454 | au_writel(0x00000000, IC1_TESTBIT); | ||
455 | |||
456 | /* Initialize IC0, which is fixed per processor. | ||
457 | */ | ||
458 | imp = au1xxx_ic0_map; | ||
459 | for (i=0; i<au1xxx_ic0_nr_irqs; i++) { | ||
460 | setup_local_irq(imp->im_irq, imp->im_type, imp->im_request); | ||
461 | imp++; | ||
462 | } | ||
463 | |||
464 | /* Now set up the irq mapping for the board. | ||
465 | */ | ||
466 | imp = au1xxx_irq_map; | ||
467 | for (i=0; i<au1xxx_nr_irqs; i++) { | ||
468 | setup_local_irq(imp->im_irq, imp->im_type, imp->im_request); | ||
469 | imp++; | ||
470 | } | ||
471 | |||
472 | set_c0_status(ALLINTS); | ||
473 | |||
474 | /* Board specific IRQ initialization. | ||
475 | */ | ||
476 | if (board_init_irq) | ||
477 | (*board_init_irq)(); | ||
478 | } | ||
479 | |||
480 | |||
481 | /* | ||
482 | * Interrupts are nested. Even if an interrupt handler is registered | ||
483 | * as "fast", we might get another interrupt before we return from | ||
484 | * intcX_reqX_irqdispatch(). | ||
485 | */ | ||
486 | |||
487 | void intc0_req0_irqdispatch(struct pt_regs *regs) | ||
488 | { | ||
489 | int irq = 0; | ||
490 | static unsigned long intc0_req0 = 0; | ||
491 | |||
492 | intc0_req0 |= au_readl(IC0_REQ0INT); | ||
493 | |||
494 | if (!intc0_req0) return; | ||
495 | |||
496 | /* | ||
497 | * Because of the tight timing of SETUP token to reply | ||
498 | * transactions, the USB devices-side packet complete | ||
499 | * interrupt needs the highest priority. | ||
500 | */ | ||
501 | if ((intc0_req0 & (1<<AU1000_USB_DEV_REQ_INT))) { | ||
502 | intc0_req0 &= ~(1<<AU1000_USB_DEV_REQ_INT); | ||
503 | do_IRQ(AU1000_USB_DEV_REQ_INT, regs); | ||
504 | return; | ||
505 | } | ||
506 | |||
507 | irq = au_ffs(intc0_req0) - 1; | ||
508 | intc0_req0 &= ~(1<<irq); | ||
509 | do_IRQ(irq, regs); | ||
510 | } | ||
511 | |||
512 | |||
513 | void intc0_req1_irqdispatch(struct pt_regs *regs) | ||
514 | { | ||
515 | int irq = 0; | ||
516 | static unsigned long intc0_req1 = 0; | ||
517 | |||
518 | intc0_req1 |= au_readl(IC0_REQ1INT); | ||
519 | |||
520 | if (!intc0_req1) return; | ||
521 | |||
522 | irq = au_ffs(intc0_req1) - 1; | ||
523 | intc0_req1 &= ~(1<<irq); | ||
524 | #ifdef CONFIG_PM | ||
525 | if (irq == AU1000_TOY_MATCH2_INT) { | ||
526 | mask_and_ack_rise_edge_irq(irq); | ||
527 | counter0_irq(irq, NULL, regs); | ||
528 | local_enable_irq(irq); | ||
529 | } | ||
530 | else | ||
531 | #endif | ||
532 | { | ||
533 | do_IRQ(irq, regs); | ||
534 | } | ||
535 | } | ||
536 | |||
537 | |||
538 | /* | ||
539 | * Interrupt Controller 1: | ||
540 | * interrupts 32 - 63 | ||
541 | */ | ||
542 | void intc1_req0_irqdispatch(struct pt_regs *regs) | ||
543 | { | ||
544 | int irq = 0; | ||
545 | static unsigned long intc1_req0 = 0; | ||
546 | |||
547 | intc1_req0 |= au_readl(IC1_REQ0INT); | ||
548 | |||
549 | if (!intc1_req0) return; | ||
550 | |||
551 | irq = au_ffs(intc1_req0) - 1; | ||
552 | intc1_req0 &= ~(1<<irq); | ||
553 | irq += 32; | ||
554 | do_IRQ(irq, regs); | ||
555 | } | ||
556 | |||
557 | |||
558 | void intc1_req1_irqdispatch(struct pt_regs *regs) | ||
559 | { | ||
560 | int irq = 0; | ||
561 | static unsigned long intc1_req1 = 0; | ||
562 | |||
563 | intc1_req1 |= au_readl(IC1_REQ1INT); | ||
564 | |||
565 | if (!intc1_req1) return; | ||
566 | |||
567 | irq = au_ffs(intc1_req1) - 1; | ||
568 | intc1_req1 &= ~(1<<irq); | ||
569 | irq += 32; | ||
570 | do_IRQ(irq, regs); | ||
571 | } | ||
572 | |||
573 | #ifdef CONFIG_PM | ||
574 | |||
575 | /* Save/restore the interrupt controller state. | ||
576 | * Called from the save/restore core registers as part of the | ||
577 | * au_sleep function in power.c.....maybe I should just pm_register() | ||
578 | * them instead? | ||
579 | */ | ||
580 | static uint sleep_intctl_config0[2]; | ||
581 | static uint sleep_intctl_config1[2]; | ||
582 | static uint sleep_intctl_config2[2]; | ||
583 | static uint sleep_intctl_src[2]; | ||
584 | static uint sleep_intctl_assign[2]; | ||
585 | static uint sleep_intctl_wake[2]; | ||
586 | static uint sleep_intctl_mask[2]; | ||
587 | |||
588 | void | ||
589 | save_au1xxx_intctl(void) | ||
590 | { | ||
591 | sleep_intctl_config0[0] = au_readl(IC0_CFG0RD); | ||
592 | sleep_intctl_config1[0] = au_readl(IC0_CFG1RD); | ||
593 | sleep_intctl_config2[0] = au_readl(IC0_CFG2RD); | ||
594 | sleep_intctl_src[0] = au_readl(IC0_SRCRD); | ||
595 | sleep_intctl_assign[0] = au_readl(IC0_ASSIGNRD); | ||
596 | sleep_intctl_wake[0] = au_readl(IC0_WAKERD); | ||
597 | sleep_intctl_mask[0] = au_readl(IC0_MASKRD); | ||
598 | |||
599 | sleep_intctl_config0[1] = au_readl(IC1_CFG0RD); | ||
600 | sleep_intctl_config1[1] = au_readl(IC1_CFG1RD); | ||
601 | sleep_intctl_config2[1] = au_readl(IC1_CFG2RD); | ||
602 | sleep_intctl_src[1] = au_readl(IC1_SRCRD); | ||
603 | sleep_intctl_assign[1] = au_readl(IC1_ASSIGNRD); | ||
604 | sleep_intctl_wake[1] = au_readl(IC1_WAKERD); | ||
605 | sleep_intctl_mask[1] = au_readl(IC1_MASKRD); | ||
606 | } | ||
607 | |||
608 | /* For most restore operations, we clear the entire register and | ||
609 | * then set the bits we found during the save. | ||
610 | */ | ||
611 | void | ||
612 | restore_au1xxx_intctl(void) | ||
613 | { | ||
614 | au_writel(0xffffffff, IC0_MASKCLR); au_sync(); | ||
615 | |||
616 | au_writel(0xffffffff, IC0_CFG0CLR); au_sync(); | ||
617 | au_writel(sleep_intctl_config0[0], IC0_CFG0SET); au_sync(); | ||
618 | au_writel(0xffffffff, IC0_CFG1CLR); au_sync(); | ||
619 | au_writel(sleep_intctl_config1[0], IC0_CFG1SET); au_sync(); | ||
620 | au_writel(0xffffffff, IC0_CFG2CLR); au_sync(); | ||
621 | au_writel(sleep_intctl_config2[0], IC0_CFG2SET); au_sync(); | ||
622 | au_writel(0xffffffff, IC0_SRCCLR); au_sync(); | ||
623 | au_writel(sleep_intctl_src[0], IC0_SRCSET); au_sync(); | ||
624 | au_writel(0xffffffff, IC0_ASSIGNCLR); au_sync(); | ||
625 | au_writel(sleep_intctl_assign[0], IC0_ASSIGNSET); au_sync(); | ||
626 | au_writel(0xffffffff, IC0_WAKECLR); au_sync(); | ||
627 | au_writel(sleep_intctl_wake[0], IC0_WAKESET); au_sync(); | ||
628 | au_writel(0xffffffff, IC0_RISINGCLR); au_sync(); | ||
629 | au_writel(0xffffffff, IC0_FALLINGCLR); au_sync(); | ||
630 | au_writel(0x00000000, IC0_TESTBIT); au_sync(); | ||
631 | |||
632 | au_writel(0xffffffff, IC1_MASKCLR); au_sync(); | ||
633 | |||
634 | au_writel(0xffffffff, IC1_CFG0CLR); au_sync(); | ||
635 | au_writel(sleep_intctl_config0[1], IC1_CFG0SET); au_sync(); | ||
636 | au_writel(0xffffffff, IC1_CFG1CLR); au_sync(); | ||
637 | au_writel(sleep_intctl_config1[1], IC1_CFG1SET); au_sync(); | ||
638 | au_writel(0xffffffff, IC1_CFG2CLR); au_sync(); | ||
639 | au_writel(sleep_intctl_config2[1], IC1_CFG2SET); au_sync(); | ||
640 | au_writel(0xffffffff, IC1_SRCCLR); au_sync(); | ||
641 | au_writel(sleep_intctl_src[1], IC1_SRCSET); au_sync(); | ||
642 | au_writel(0xffffffff, IC1_ASSIGNCLR); au_sync(); | ||
643 | au_writel(sleep_intctl_assign[1], IC1_ASSIGNSET); au_sync(); | ||
644 | au_writel(0xffffffff, IC1_WAKECLR); au_sync(); | ||
645 | au_writel(sleep_intctl_wake[1], IC1_WAKESET); au_sync(); | ||
646 | au_writel(0xffffffff, IC1_RISINGCLR); au_sync(); | ||
647 | au_writel(0xffffffff, IC1_FALLINGCLR); au_sync(); | ||
648 | au_writel(0x00000000, IC1_TESTBIT); au_sync(); | ||
649 | |||
650 | au_writel(sleep_intctl_mask[1], IC1_MASKSET); au_sync(); | ||
651 | |||
652 | au_writel(sleep_intctl_mask[0], IC0_MASKSET); au_sync(); | ||
653 | } | ||
654 | #endif /* CONFIG_PM */ | ||
diff --git a/arch/mips/au1000/common/pci.c b/arch/mips/au1000/common/pci.c new file mode 100644 index 000000000000..533721eef6ae --- /dev/null +++ b/arch/mips/au1000/common/pci.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * BRIEF MODULE DESCRIPTION | ||
3 | * Alchemy/AMD Au1x00 pci support. | ||
4 | * | ||
5 | * Copyright 2001,2002,2003 MontaVista Software Inc. | ||
6 | * Author: MontaVista Software, Inc. | ||
7 | * ppopov@mvista.com or source@mvista.com | ||
8 | * | ||
9 | * Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org) | ||
10 | * | ||
11 | * Support for all devices (greater than 16) added by David Gathright. | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify it | ||
14 | * under the terms of the GNU General Public License as published by the | ||
15 | * Free Software Foundation; either version 2 of the License, or (at your | ||
16 | * option) any later version. | ||
17 | * | ||
18 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
19 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
20 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
21 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
24 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
25 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | * | ||
29 | * You should have received a copy of the GNU General Public License along | ||
30 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
31 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
32 | */ | ||
33 | #include <linux/config.h> | ||
34 | #include <linux/types.h> | ||
35 | #include <linux/pci.h> | ||
36 | #include <linux/kernel.h> | ||
37 | #include <linux/init.h> | ||
38 | |||
39 | #include <asm/mach-au1x00/au1000.h> | ||
40 | |||
41 | /* TBD */ | ||
42 | static struct resource pci_io_resource = { | ||
43 | "pci IO space", | ||
44 | (u32)PCI_IO_START, | ||
45 | (u32)PCI_IO_END, | ||
46 | IORESOURCE_IO | ||
47 | }; | ||
48 | |||
49 | static struct resource pci_mem_resource = { | ||
50 | "pci memory space", | ||
51 | (u32)PCI_MEM_START, | ||
52 | (u32)PCI_MEM_END, | ||
53 | IORESOURCE_MEM | ||
54 | }; | ||
55 | |||
56 | extern struct pci_ops au1x_pci_ops; | ||
57 | |||
58 | static struct pci_controller au1x_controller = { | ||
59 | .pci_ops = &au1x_pci_ops, | ||
60 | .io_resource = &pci_io_resource, | ||
61 | .mem_resource = &pci_mem_resource, | ||
62 | }; | ||
63 | |||
64 | #if defined(CONFIG_SOC_AU1500) || defined(CONFIG_SOC_AU1550) | ||
65 | static unsigned long virt_io_addr; | ||
66 | #endif | ||
67 | |||
68 | static int __init au1x_pci_setup(void) | ||
69 | { | ||
70 | #if defined(CONFIG_SOC_AU1500) || defined(CONFIG_SOC_AU1550) | ||
71 | virt_io_addr = (unsigned long)ioremap(Au1500_PCI_IO_START, | ||
72 | Au1500_PCI_IO_END - Au1500_PCI_IO_START + 1); | ||
73 | |||
74 | if (!virt_io_addr) { | ||
75 | printk(KERN_ERR "Unable to ioremap pci space\n"); | ||
76 | return 1; | ||
77 | } | ||
78 | |||
79 | #ifdef CONFIG_DMA_NONCOHERENT | ||
80 | /* | ||
81 | * Set the NC bit in controller for Au1500 pre-AC silicon | ||
82 | */ | ||
83 | u32 prid = read_c0_prid(); | ||
84 | if ( (prid & 0xFF000000) == 0x01000000 && prid < 0x01030202) { | ||
85 | au_writel( 1<<16 | au_readl(Au1500_PCI_CFG), Au1500_PCI_CFG); | ||
86 | printk("Non-coherent PCI accesses enabled\n"); | ||
87 | } | ||
88 | #endif | ||
89 | |||
90 | set_io_port_base(virt_io_addr); | ||
91 | #endif | ||
92 | |||
93 | register_pci_controller(&au1x_controller); | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | arch_initcall(au1x_pci_setup); | ||
diff --git a/arch/mips/au1000/common/platform.c b/arch/mips/au1000/common/platform.c new file mode 100644 index 000000000000..0776b2db5641 --- /dev/null +++ b/arch/mips/au1000/common/platform.c | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * Platform device support for Au1x00 SoCs. | ||
3 | * | ||
4 | * Copyright 2004, Matt Porter <mporter@kernel.crashing.org> | ||
5 | * | ||
6 | * This file is licensed under the terms of the GNU General Public | ||
7 | * License version 2. This program is licensed "as is" without any | ||
8 | * warranty of any kind, whether express or implied. | ||
9 | */ | ||
10 | #include <linux/device.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/resource.h> | ||
14 | |||
15 | #include <asm/mach-au1x00/au1000.h> | ||
16 | |||
17 | static struct resource au1xxx_usb_ohci_resources[] = { | ||
18 | [0] = { | ||
19 | .start = USB_OHCI_BASE, | ||
20 | .end = USB_OHCI_BASE + USB_OHCI_LEN, | ||
21 | .flags = IORESOURCE_MEM, | ||
22 | }, | ||
23 | [1] = { | ||
24 | .start = AU1000_USB_HOST_INT, | ||
25 | .end = AU1000_USB_HOST_INT, | ||
26 | .flags = IORESOURCE_IRQ, | ||
27 | }, | ||
28 | }; | ||
29 | |||
30 | /* The dmamask must be set for OHCI to work */ | ||
31 | static u64 ohci_dmamask = ~(u32)0; | ||
32 | |||
33 | static struct platform_device au1xxx_usb_ohci_device = { | ||
34 | .name = "au1xxx-ohci", | ||
35 | .id = 0, | ||
36 | .dev = { | ||
37 | .dma_mask = &ohci_dmamask, | ||
38 | .coherent_dma_mask = 0xffffffff, | ||
39 | }, | ||
40 | .num_resources = ARRAY_SIZE(au1xxx_usb_ohci_resources), | ||
41 | .resource = au1xxx_usb_ohci_resources, | ||
42 | }; | ||
43 | |||
44 | static struct platform_device *au1xxx_platform_devices[] __initdata = { | ||
45 | &au1xxx_usb_ohci_device, | ||
46 | }; | ||
47 | |||
48 | int au1xxx_platform_init(void) | ||
49 | { | ||
50 | return platform_add_devices(au1xxx_platform_devices, ARRAY_SIZE(au1xxx_platform_devices)); | ||
51 | } | ||
52 | |||
53 | arch_initcall(au1xxx_platform_init); | ||
diff --git a/arch/mips/au1000/common/power.c b/arch/mips/au1000/common/power.c new file mode 100644 index 000000000000..c40daccbb5b1 --- /dev/null +++ b/arch/mips/au1000/common/power.c | |||
@@ -0,0 +1,493 @@ | |||
1 | /* | ||
2 | * BRIEF MODULE DESCRIPTION | ||
3 | * Au1000 Power Management routines. | ||
4 | * | ||
5 | * Copyright 2001 MontaVista Software Inc. | ||
6 | * Author: MontaVista Software, Inc. | ||
7 | * ppopov@mvista.com or source@mvista.com | ||
8 | * | ||
9 | * Some of the routines are right out of init/main.c, whose | ||
10 | * copyrights apply here. | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify it | ||
13 | * under the terms of the GNU General Public License as published by the | ||
14 | * Free Software Foundation; either version 2 of the License, or (at your | ||
15 | * option) any later version. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
19 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
20 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
23 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
24 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
27 | * | ||
28 | * You should have received a copy of the GNU General Public License along | ||
29 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
30 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
31 | */ | ||
32 | #include <linux/config.h> | ||
33 | #include <linux/init.h> | ||
34 | #include <linux/pm.h> | ||
35 | #include <linux/slab.h> | ||
36 | #include <linux/sysctl.h> | ||
37 | |||
38 | #include <asm/string.h> | ||
39 | #include <asm/uaccess.h> | ||
40 | #include <asm/io.h> | ||
41 | #include <asm/system.h> | ||
42 | #include <asm/mach-au1x00/au1000.h> | ||
43 | |||
44 | #ifdef CONFIG_PM | ||
45 | |||
46 | #define DEBUG 1 | ||
47 | #ifdef DEBUG | ||
48 | # define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args) | ||
49 | #else | ||
50 | # define DPRINTK(fmt, args...) | ||
51 | #endif | ||
52 | |||
53 | static void calibrate_delay(void); | ||
54 | |||
55 | extern void set_au1x00_speed(unsigned int new_freq); | ||
56 | extern unsigned int get_au1x00_speed(void); | ||
57 | extern unsigned long get_au1x00_uart_baud_base(void); | ||
58 | extern void set_au1x00_uart_baud_base(unsigned long new_baud_base); | ||
59 | extern unsigned long save_local_and_disable(int controller); | ||
60 | extern void restore_local_and_enable(int controller, unsigned long mask); | ||
61 | extern void local_enable_irq(unsigned int irq_nr); | ||
62 | |||
63 | /* Quick acpi hack. This will have to change! */ | ||
64 | #define CTL_ACPI 9999 | ||
65 | #define ACPI_S1_SLP_TYP 19 | ||
66 | #define ACPI_SLEEP 21 | ||
67 | |||
68 | |||
69 | static DEFINE_SPINLOCK(pm_lock); | ||
70 | |||
71 | /* We need to save/restore a bunch of core registers that are | ||
72 | * either volatile or reset to some state across a processor sleep. | ||
73 | * If reading a register doesn't provide a proper result for a | ||
74 | * later restore, we have to provide a function for loading that | ||
75 | * register and save a copy. | ||
76 | * | ||
77 | * We only have to save/restore registers that aren't otherwise | ||
78 | * done as part of a driver pm_* function. | ||
79 | */ | ||
80 | static uint sleep_aux_pll_cntrl; | ||
81 | static uint sleep_cpu_pll_cntrl; | ||
82 | static uint sleep_pin_function; | ||
83 | static uint sleep_uart0_inten; | ||
84 | static uint sleep_uart0_fifoctl; | ||
85 | static uint sleep_uart0_linectl; | ||
86 | static uint sleep_uart0_clkdiv; | ||
87 | static uint sleep_uart0_enable; | ||
88 | static uint sleep_usbhost_enable; | ||
89 | static uint sleep_usbdev_enable; | ||
90 | static uint sleep_static_memctlr[4][3]; | ||
91 | |||
92 | /* Define this to cause the value you write to /proc/sys/pm/sleep to | ||
93 | * set the TOY timer for the amount of time you want to sleep. | ||
94 | * This is done mainly for testing, but may be useful in other cases. | ||
95 | * The value is number of 32KHz ticks to sleep. | ||
96 | */ | ||
97 | #define SLEEP_TEST_TIMEOUT 1 | ||
98 | #ifdef SLEEP_TEST_TIMEOUT | ||
99 | static int sleep_ticks; | ||
100 | void wakeup_counter0_set(int ticks); | ||
101 | #endif | ||
102 | |||
103 | static void | ||
104 | save_core_regs(void) | ||
105 | { | ||
106 | extern void save_au1xxx_intctl(void); | ||
107 | extern void pm_eth0_shutdown(void); | ||
108 | |||
109 | /* Do the serial ports.....these really should be a pm_* | ||
110 | * registered function by the driver......but of course the | ||
111 | * standard serial driver doesn't understand our Au1xxx | ||
112 | * unique registers. | ||
113 | */ | ||
114 | sleep_uart0_inten = au_readl(UART0_ADDR + UART_IER); | ||
115 | sleep_uart0_fifoctl = au_readl(UART0_ADDR + UART_FCR); | ||
116 | sleep_uart0_linectl = au_readl(UART0_ADDR + UART_LCR); | ||
117 | sleep_uart0_clkdiv = au_readl(UART0_ADDR + UART_CLK); | ||
118 | sleep_uart0_enable = au_readl(UART0_ADDR + UART_MOD_CNTRL); | ||
119 | |||
120 | /* Shutdown USB host/device. | ||
121 | */ | ||
122 | sleep_usbhost_enable = au_readl(USB_HOST_CONFIG); | ||
123 | |||
124 | /* There appears to be some undocumented reset register.... | ||
125 | */ | ||
126 | au_writel(0, 0xb0100004); au_sync(); | ||
127 | au_writel(0, USB_HOST_CONFIG); au_sync(); | ||
128 | |||
129 | sleep_usbdev_enable = au_readl(USBD_ENABLE); | ||
130 | au_writel(0, USBD_ENABLE); au_sync(); | ||
131 | |||
132 | /* Save interrupt controller state. | ||
133 | */ | ||
134 | save_au1xxx_intctl(); | ||
135 | |||
136 | /* Clocks and PLLs. | ||
137 | */ | ||
138 | sleep_aux_pll_cntrl = au_readl(SYS_AUXPLL); | ||
139 | |||
140 | /* We don't really need to do this one, but unless we | ||
141 | * write it again it won't have a valid value if we | ||
142 | * happen to read it. | ||
143 | */ | ||
144 | sleep_cpu_pll_cntrl = au_readl(SYS_CPUPLL); | ||
145 | |||
146 | sleep_pin_function = au_readl(SYS_PINFUNC); | ||
147 | |||
148 | /* Save the static memory controller configuration. | ||
149 | */ | ||
150 | sleep_static_memctlr[0][0] = au_readl(MEM_STCFG0); | ||
151 | sleep_static_memctlr[0][1] = au_readl(MEM_STTIME0); | ||
152 | sleep_static_memctlr[0][2] = au_readl(MEM_STADDR0); | ||
153 | sleep_static_memctlr[1][0] = au_readl(MEM_STCFG1); | ||
154 | sleep_static_memctlr[1][1] = au_readl(MEM_STTIME1); | ||
155 | sleep_static_memctlr[1][2] = au_readl(MEM_STADDR1); | ||
156 | sleep_static_memctlr[2][0] = au_readl(MEM_STCFG2); | ||
157 | sleep_static_memctlr[2][1] = au_readl(MEM_STTIME2); | ||
158 | sleep_static_memctlr[2][2] = au_readl(MEM_STADDR2); | ||
159 | sleep_static_memctlr[3][0] = au_readl(MEM_STCFG3); | ||
160 | sleep_static_memctlr[3][1] = au_readl(MEM_STTIME3); | ||
161 | sleep_static_memctlr[3][2] = au_readl(MEM_STADDR3); | ||
162 | } | ||
163 | |||
164 | static void | ||
165 | restore_core_regs(void) | ||
166 | { | ||
167 | extern void restore_au1xxx_intctl(void); | ||
168 | extern void wakeup_counter0_adjust(void); | ||
169 | |||
170 | au_writel(sleep_aux_pll_cntrl, SYS_AUXPLL); au_sync(); | ||
171 | au_writel(sleep_cpu_pll_cntrl, SYS_CPUPLL); au_sync(); | ||
172 | au_writel(sleep_pin_function, SYS_PINFUNC); au_sync(); | ||
173 | |||
174 | /* Restore the static memory controller configuration. | ||
175 | */ | ||
176 | au_writel(sleep_static_memctlr[0][0], MEM_STCFG0); | ||
177 | au_writel(sleep_static_memctlr[0][1], MEM_STTIME0); | ||
178 | au_writel(sleep_static_memctlr[0][2], MEM_STADDR0); | ||
179 | au_writel(sleep_static_memctlr[1][0], MEM_STCFG1); | ||
180 | au_writel(sleep_static_memctlr[1][1], MEM_STTIME1); | ||
181 | au_writel(sleep_static_memctlr[1][2], MEM_STADDR1); | ||
182 | au_writel(sleep_static_memctlr[2][0], MEM_STCFG2); | ||
183 | au_writel(sleep_static_memctlr[2][1], MEM_STTIME2); | ||
184 | au_writel(sleep_static_memctlr[2][2], MEM_STADDR2); | ||
185 | au_writel(sleep_static_memctlr[3][0], MEM_STCFG3); | ||
186 | au_writel(sleep_static_memctlr[3][1], MEM_STTIME3); | ||
187 | au_writel(sleep_static_memctlr[3][2], MEM_STADDR3); | ||
188 | |||
189 | /* Enable the UART if it was enabled before sleep. | ||
190 | * I guess I should define module control bits........ | ||
191 | */ | ||
192 | if (sleep_uart0_enable & 0x02) { | ||
193 | au_writel(0, UART0_ADDR + UART_MOD_CNTRL); au_sync(); | ||
194 | au_writel(1, UART0_ADDR + UART_MOD_CNTRL); au_sync(); | ||
195 | au_writel(3, UART0_ADDR + UART_MOD_CNTRL); au_sync(); | ||
196 | au_writel(sleep_uart0_inten, UART0_ADDR + UART_IER); au_sync(); | ||
197 | au_writel(sleep_uart0_fifoctl, UART0_ADDR + UART_FCR); au_sync(); | ||
198 | au_writel(sleep_uart0_linectl, UART0_ADDR + UART_LCR); au_sync(); | ||
199 | au_writel(sleep_uart0_clkdiv, UART0_ADDR + UART_CLK); au_sync(); | ||
200 | } | ||
201 | |||
202 | restore_au1xxx_intctl(); | ||
203 | wakeup_counter0_adjust(); | ||
204 | } | ||
205 | |||
206 | unsigned long suspend_mode; | ||
207 | |||
208 | void wakeup_from_suspend(void) | ||
209 | { | ||
210 | suspend_mode = 0; | ||
211 | } | ||
212 | |||
213 | int au_sleep(void) | ||
214 | { | ||
215 | unsigned long wakeup, flags; | ||
216 | extern void save_and_sleep(void); | ||
217 | |||
218 | spin_lock_irqsave(&pm_lock,flags); | ||
219 | |||
220 | save_core_regs(); | ||
221 | |||
222 | flush_cache_all(); | ||
223 | |||
224 | /** The code below is all system dependent and we should probably | ||
225 | ** have a function call out of here to set this up. You need | ||
226 | ** to configure the GPIO or timer interrupts that will bring | ||
227 | ** you out of sleep. | ||
228 | ** For testing, the TOY counter wakeup is useful. | ||
229 | **/ | ||
230 | |||
231 | #if 0 | ||
232 | au_writel(au_readl(SYS_PINSTATERD) & ~(1 << 11), SYS_PINSTATERD); | ||
233 | |||
234 | /* gpio 6 can cause a wake up event */ | ||
235 | wakeup = au_readl(SYS_WAKEMSK); | ||
236 | wakeup &= ~(1 << 8); /* turn off match20 wakeup */ | ||
237 | wakeup |= 1 << 6; /* turn on gpio 6 wakeup */ | ||
238 | #else | ||
239 | /* For testing, allow match20 to wake us up. | ||
240 | */ | ||
241 | #ifdef SLEEP_TEST_TIMEOUT | ||
242 | wakeup_counter0_set(sleep_ticks); | ||
243 | #endif | ||
244 | wakeup = 1 << 8; /* turn on match20 wakeup */ | ||
245 | wakeup = 0; | ||
246 | #endif | ||
247 | au_writel(1, SYS_WAKESRC); /* clear cause */ | ||
248 | au_sync(); | ||
249 | au_writel(wakeup, SYS_WAKEMSK); | ||
250 | au_sync(); | ||
251 | |||
252 | save_and_sleep(); | ||
253 | |||
254 | /* after a wakeup, the cpu vectors back to 0x1fc00000 so | ||
255 | * it's up to the boot code to get us back here. | ||
256 | */ | ||
257 | restore_core_regs(); | ||
258 | spin_unlock_irqrestore(&pm_lock, flags); | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | static int pm_do_sleep(ctl_table * ctl, int write, struct file *file, | ||
263 | void *buffer, size_t * len) | ||
264 | { | ||
265 | int retval = 0; | ||
266 | #ifdef SLEEP_TEST_TIMEOUT | ||
267 | #define TMPBUFLEN2 16 | ||
268 | char buf[TMPBUFLEN2], *p; | ||
269 | #endif | ||
270 | |||
271 | if (!write) { | ||
272 | *len = 0; | ||
273 | } else { | ||
274 | #ifdef SLEEP_TEST_TIMEOUT | ||
275 | if (*len > TMPBUFLEN2 - 1) { | ||
276 | return -EFAULT; | ||
277 | } | ||
278 | if (copy_from_user(buf, buffer, *len)) { | ||
279 | return -EFAULT; | ||
280 | } | ||
281 | buf[*len] = 0; | ||
282 | p = buf; | ||
283 | sleep_ticks = simple_strtoul(p, &p, 0); | ||
284 | #endif | ||
285 | retval = pm_send_all(PM_SUSPEND, (void *) 2); | ||
286 | |||
287 | if (retval) | ||
288 | return retval; | ||
289 | |||
290 | au_sleep(); | ||
291 | retval = pm_send_all(PM_RESUME, (void *) 0); | ||
292 | } | ||
293 | return retval; | ||
294 | } | ||
295 | |||
296 | static int pm_do_suspend(ctl_table * ctl, int write, struct file *file, | ||
297 | void *buffer, size_t * len) | ||
298 | { | ||
299 | int retval = 0; | ||
300 | void au1k_wait(void); | ||
301 | |||
302 | if (!write) { | ||
303 | *len = 0; | ||
304 | } else { | ||
305 | retval = pm_send_all(PM_SUSPEND, (void *) 2); | ||
306 | if (retval) | ||
307 | return retval; | ||
308 | suspend_mode = 1; | ||
309 | au1k_wait(); | ||
310 | retval = pm_send_all(PM_RESUME, (void *) 0); | ||
311 | } | ||
312 | return retval; | ||
313 | } | ||
314 | |||
315 | |||
316 | static int pm_do_freq(ctl_table * ctl, int write, struct file *file, | ||
317 | void *buffer, size_t * len) | ||
318 | { | ||
319 | int retval = 0, i; | ||
320 | unsigned long val, pll; | ||
321 | #define TMPBUFLEN 64 | ||
322 | #define MAX_CPU_FREQ 396 | ||
323 | char buf[TMPBUFLEN], *p; | ||
324 | unsigned long flags, intc0_mask, intc1_mask; | ||
325 | unsigned long old_baud_base, old_cpu_freq, baud_rate, old_clk, | ||
326 | old_refresh; | ||
327 | unsigned long new_baud_base, new_cpu_freq, new_clk, new_refresh; | ||
328 | |||
329 | spin_lock_irqsave(&pm_lock, flags); | ||
330 | if (!write) { | ||
331 | *len = 0; | ||
332 | } else { | ||
333 | /* Parse the new frequency */ | ||
334 | if (*len > TMPBUFLEN - 1) { | ||
335 | spin_unlock_irqrestore(&pm_lock, flags); | ||
336 | return -EFAULT; | ||
337 | } | ||
338 | if (copy_from_user(buf, buffer, *len)) { | ||
339 | spin_unlock_irqrestore(&pm_lock, flags); | ||
340 | return -EFAULT; | ||
341 | } | ||
342 | buf[*len] = 0; | ||
343 | p = buf; | ||
344 | val = simple_strtoul(p, &p, 0); | ||
345 | if (val > MAX_CPU_FREQ) { | ||
346 | spin_unlock_irqrestore(&pm_lock, flags); | ||
347 | return -EFAULT; | ||
348 | } | ||
349 | |||
350 | pll = val / 12; | ||
351 | if ((pll > 33) || (pll < 7)) { /* 396 MHz max, 84 MHz min */ | ||
352 | /* revisit this for higher speed cpus */ | ||
353 | spin_unlock_irqrestore(&pm_lock, flags); | ||
354 | return -EFAULT; | ||
355 | } | ||
356 | |||
357 | old_baud_base = get_au1x00_uart_baud_base(); | ||
358 | old_cpu_freq = get_au1x00_speed(); | ||
359 | |||
360 | new_cpu_freq = pll * 12 * 1000000; | ||
361 | new_baud_base = (new_cpu_freq / (2 * ((int)(au_readl(SYS_POWERCTRL)&0x03) + 2) * 16)); | ||
362 | set_au1x00_speed(new_cpu_freq); | ||
363 | set_au1x00_uart_baud_base(new_baud_base); | ||
364 | |||
365 | old_refresh = au_readl(MEM_SDREFCFG) & 0x1ffffff; | ||
366 | new_refresh = | ||
367 | ((old_refresh * new_cpu_freq) / | ||
368 | old_cpu_freq) | (au_readl(MEM_SDREFCFG) & ~0x1ffffff); | ||
369 | |||
370 | au_writel(pll, SYS_CPUPLL); | ||
371 | au_sync_delay(1); | ||
372 | au_writel(new_refresh, MEM_SDREFCFG); | ||
373 | au_sync_delay(1); | ||
374 | |||
375 | for (i = 0; i < 4; i++) { | ||
376 | if (au_readl | ||
377 | (UART_BASE + UART_MOD_CNTRL + | ||
378 | i * 0x00100000) == 3) { | ||
379 | old_clk = | ||
380 | au_readl(UART_BASE + UART_CLK + | ||
381 | i * 0x00100000); | ||
382 | // baud_rate = baud_base/clk | ||
383 | baud_rate = old_baud_base / old_clk; | ||
384 | /* we won't get an exact baud rate and the error | ||
385 | * could be significant enough that our new | ||
386 | * calculation will result in a clock that will | ||
387 | * give us a baud rate that's too far off from | ||
388 | * what we really want. | ||
389 | */ | ||
390 | if (baud_rate > 100000) | ||
391 | baud_rate = 115200; | ||
392 | else if (baud_rate > 50000) | ||
393 | baud_rate = 57600; | ||
394 | else if (baud_rate > 30000) | ||
395 | baud_rate = 38400; | ||
396 | else if (baud_rate > 17000) | ||
397 | baud_rate = 19200; | ||
398 | else | ||
399 | (baud_rate = 9600); | ||
400 | // new_clk = new_baud_base/baud_rate | ||
401 | new_clk = new_baud_base / baud_rate; | ||
402 | au_writel(new_clk, | ||
403 | UART_BASE + UART_CLK + | ||
404 | i * 0x00100000); | ||
405 | au_sync_delay(10); | ||
406 | } | ||
407 | } | ||
408 | } | ||
409 | |||
410 | |||
411 | /* We don't want _any_ interrupts other than | ||
412 | * match20. Otherwise our calibrate_delay() | ||
413 | * calculation will be off, potentially a lot. | ||
414 | */ | ||
415 | intc0_mask = save_local_and_disable(0); | ||
416 | intc1_mask = save_local_and_disable(1); | ||
417 | local_enable_irq(AU1000_TOY_MATCH2_INT); | ||
418 | spin_unlock_irqrestore(&pm_lock, flags); | ||
419 | calibrate_delay(); | ||
420 | restore_local_and_enable(0, intc0_mask); | ||
421 | restore_local_and_enable(1, intc1_mask); | ||
422 | return retval; | ||
423 | } | ||
424 | |||
425 | |||
426 | static struct ctl_table pm_table[] = { | ||
427 | {ACPI_S1_SLP_TYP, "suspend", NULL, 0, 0600, NULL, &pm_do_suspend}, | ||
428 | {ACPI_SLEEP, "sleep", NULL, 0, 0600, NULL, &pm_do_sleep}, | ||
429 | {CTL_ACPI, "freq", NULL, 0, 0600, NULL, &pm_do_freq}, | ||
430 | {0} | ||
431 | }; | ||
432 | |||
433 | static struct ctl_table pm_dir_table[] = { | ||
434 | {CTL_ACPI, "pm", NULL, 0, 0555, pm_table}, | ||
435 | {0} | ||
436 | }; | ||
437 | |||
438 | /* | ||
439 | * Initialize power interface | ||
440 | */ | ||
441 | static int __init pm_init(void) | ||
442 | { | ||
443 | register_sysctl_table(pm_dir_table, 1); | ||
444 | return 0; | ||
445 | } | ||
446 | |||
447 | __initcall(pm_init); | ||
448 | |||
449 | |||
450 | /* | ||
451 | * This is right out of init/main.c | ||
452 | */ | ||
453 | |||
454 | /* This is the number of bits of precision for the loops_per_jiffy. Each | ||
455 | bit takes on average 1.5/HZ seconds. This (like the original) is a little | ||
456 | better than 1% */ | ||
457 | #define LPS_PREC 8 | ||
458 | |||
459 | static void calibrate_delay(void) | ||
460 | { | ||
461 | unsigned long ticks, loopbit; | ||
462 | int lps_precision = LPS_PREC; | ||
463 | |||
464 | loops_per_jiffy = (1 << 12); | ||
465 | |||
466 | while (loops_per_jiffy <<= 1) { | ||
467 | /* wait for "start of" clock tick */ | ||
468 | ticks = jiffies; | ||
469 | while (ticks == jiffies) | ||
470 | /* nothing */ ; | ||
471 | /* Go .. */ | ||
472 | ticks = jiffies; | ||
473 | __delay(loops_per_jiffy); | ||
474 | ticks = jiffies - ticks; | ||
475 | if (ticks) | ||
476 | break; | ||
477 | } | ||
478 | |||
479 | /* Do a binary approximation to get loops_per_jiffy set to equal one clock | ||
480 | (up to lps_precision bits) */ | ||
481 | loops_per_jiffy >>= 1; | ||
482 | loopbit = loops_per_jiffy; | ||
483 | while (lps_precision-- && (loopbit >>= 1)) { | ||
484 | loops_per_jiffy |= loopbit; | ||
485 | ticks = jiffies; | ||
486 | while (ticks == jiffies); | ||
487 | ticks = jiffies; | ||
488 | __delay(loops_per_jiffy); | ||
489 | if (jiffies != ticks) /* longer than 1 tick */ | ||
490 | loops_per_jiffy &= ~loopbit; | ||
491 | } | ||
492 | } | ||
493 | #endif /* CONFIG_PM */ | ||
diff --git a/arch/mips/au1000/common/prom.c b/arch/mips/au1000/common/prom.c new file mode 100644 index 000000000000..22e5a85af4d5 --- /dev/null +++ b/arch/mips/au1000/common/prom.c | |||
@@ -0,0 +1,162 @@ | |||
1 | /* | ||
2 | * | ||
3 | * BRIEF MODULE DESCRIPTION | ||
4 | * PROM library initialisation code, assuming a version of | ||
5 | * pmon is the boot code. | ||
6 | * | ||
7 | * Copyright 2000,2001 MontaVista Software Inc. | ||
8 | * Author: MontaVista Software, Inc. | ||
9 | * ppopov@mvista.com or source@mvista.com | ||
10 | * | ||
11 | * This file was derived from Carsten Langgaard's | ||
12 | * arch/mips/mips-boards/xx files. | ||
13 | * | ||
14 | * Carsten Langgaard, carstenl@mips.com | ||
15 | * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. | ||
16 | * | ||
17 | * This program is free software; you can redistribute it and/or modify it | ||
18 | * under the terms of the GNU General Public License as published by the | ||
19 | * Free Software Foundation; either version 2 of the License, or (at your | ||
20 | * option) any later version. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
23 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
24 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
25 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
27 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
28 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
29 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
31 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
32 | * | ||
33 | * You should have received a copy of the GNU General Public License along | ||
34 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
35 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
36 | */ | ||
37 | |||
38 | #include <linux/module.h> | ||
39 | #include <linux/kernel.h> | ||
40 | #include <linux/init.h> | ||
41 | #include <linux/string.h> | ||
42 | |||
43 | #include <asm/bootinfo.h> | ||
44 | |||
45 | /* #define DEBUG_CMDLINE */ | ||
46 | |||
47 | extern int prom_argc; | ||
48 | extern char **prom_argv, **prom_envp; | ||
49 | |||
50 | typedef struct | ||
51 | { | ||
52 | char *name; | ||
53 | /* char *val; */ | ||
54 | }t_env_var; | ||
55 | |||
56 | |||
57 | char * prom_getcmdline(void) | ||
58 | { | ||
59 | return &(arcs_cmdline[0]); | ||
60 | } | ||
61 | |||
62 | void prom_init_cmdline(void) | ||
63 | { | ||
64 | char *cp; | ||
65 | int actr; | ||
66 | |||
67 | actr = 1; /* Always ignore argv[0] */ | ||
68 | |||
69 | cp = &(arcs_cmdline[0]); | ||
70 | while(actr < prom_argc) { | ||
71 | strcpy(cp, prom_argv[actr]); | ||
72 | cp += strlen(prom_argv[actr]); | ||
73 | *cp++ = ' '; | ||
74 | actr++; | ||
75 | } | ||
76 | if (cp != &(arcs_cmdline[0])) /* get rid of trailing space */ | ||
77 | --cp; | ||
78 | *cp = '\0'; | ||
79 | |||
80 | } | ||
81 | |||
82 | |||
83 | char *prom_getenv(char *envname) | ||
84 | { | ||
85 | /* | ||
86 | * Return a pointer to the given environment variable. | ||
87 | * Environment variables are stored in the form of "memsize=64". | ||
88 | */ | ||
89 | |||
90 | t_env_var *env = (t_env_var *)prom_envp; | ||
91 | int i; | ||
92 | |||
93 | i = strlen(envname); | ||
94 | |||
95 | while(env->name) { | ||
96 | if(strncmp(envname, env->name, i) == 0) { | ||
97 | return(env->name + strlen(envname) + 1); | ||
98 | } | ||
99 | env++; | ||
100 | } | ||
101 | return(NULL); | ||
102 | } | ||
103 | |||
104 | inline unsigned char str2hexnum(unsigned char c) | ||
105 | { | ||
106 | if(c >= '0' && c <= '9') | ||
107 | return c - '0'; | ||
108 | if(c >= 'a' && c <= 'f') | ||
109 | return c - 'a' + 10; | ||
110 | if(c >= 'A' && c <= 'F') | ||
111 | return c - 'A' + 10; | ||
112 | return 0; /* foo */ | ||
113 | } | ||
114 | |||
115 | inline void str2eaddr(unsigned char *ea, unsigned char *str) | ||
116 | { | ||
117 | int i; | ||
118 | |||
119 | for(i = 0; i < 6; i++) { | ||
120 | unsigned char num; | ||
121 | |||
122 | if((*str == '.') || (*str == ':')) | ||
123 | str++; | ||
124 | num = str2hexnum(*str++) << 4; | ||
125 | num |= (str2hexnum(*str++)); | ||
126 | ea[i] = num; | ||
127 | } | ||
128 | } | ||
129 | |||
130 | int get_ethernet_addr(char *ethernet_addr) | ||
131 | { | ||
132 | char *ethaddr_str; | ||
133 | |||
134 | ethaddr_str = prom_getenv("ethaddr"); | ||
135 | if (!ethaddr_str) { | ||
136 | printk("ethaddr not set in boot prom\n"); | ||
137 | return -1; | ||
138 | } | ||
139 | str2eaddr(ethernet_addr, ethaddr_str); | ||
140 | |||
141 | #if 0 | ||
142 | { | ||
143 | int i; | ||
144 | |||
145 | printk("get_ethernet_addr: "); | ||
146 | for (i=0; i<5; i++) | ||
147 | printk("%02x:", (unsigned char)*(ethernet_addr+i)); | ||
148 | printk("%02x\n", *(ethernet_addr+i)); | ||
149 | } | ||
150 | #endif | ||
151 | |||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | unsigned long __init prom_free_prom_memory(void) | ||
156 | { | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | EXPORT_SYMBOL(prom_getcmdline); | ||
161 | EXPORT_SYMBOL(get_ethernet_addr); | ||
162 | EXPORT_SYMBOL(str2eaddr); | ||
diff --git a/arch/mips/au1000/common/puts.c b/arch/mips/au1000/common/puts.c new file mode 100644 index 000000000000..c2ae4624b77b --- /dev/null +++ b/arch/mips/au1000/common/puts.c | |||
@@ -0,0 +1,145 @@ | |||
1 | /* | ||
2 | * | ||
3 | * BRIEF MODULE DESCRIPTION | ||
4 | * Low level uart routines to directly access a 16550 uart. | ||
5 | * | ||
6 | * Copyright 2001 MontaVista Software Inc. | ||
7 | * Author: MontaVista Software, Inc. | ||
8 | * ppopov@mvista.com or source@mvista.com | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | * | ||
15 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
16 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
17 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
18 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
21 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
22 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
25 | * | ||
26 | * You should have received a copy of the GNU General Public License along | ||
27 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
28 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
29 | */ | ||
30 | |||
31 | #include <linux/types.h> | ||
32 | #include <asm/mach-au1x00/au1000.h> | ||
33 | |||
34 | #define SERIAL_BASE UART_BASE | ||
35 | #define SER_CMD 0x7 | ||
36 | #define SER_DATA 0x1 | ||
37 | #define TX_BUSY 0x20 | ||
38 | |||
39 | #define TIMEOUT 0xffffff | ||
40 | #define SLOW_DOWN | ||
41 | |||
42 | static const char digits[16] = "0123456789abcdef"; | ||
43 | static volatile unsigned long * const com1 = (unsigned long *)SERIAL_BASE; | ||
44 | |||
45 | |||
46 | #ifdef SLOW_DOWN | ||
47 | static inline void slow_down(void) | ||
48 | { | ||
49 | int k; | ||
50 | for (k=0; k<10000; k++); | ||
51 | } | ||
52 | #else | ||
53 | #define slow_down() | ||
54 | #endif | ||
55 | |||
56 | void | ||
57 | putch(const unsigned char c) | ||
58 | { | ||
59 | unsigned char ch; | ||
60 | int i = 0; | ||
61 | |||
62 | do { | ||
63 | ch = com1[SER_CMD]; | ||
64 | slow_down(); | ||
65 | i++; | ||
66 | if (i>TIMEOUT) { | ||
67 | break; | ||
68 | } | ||
69 | } while (0 == (ch & TX_BUSY)); | ||
70 | com1[SER_DATA] = c; | ||
71 | } | ||
72 | |||
73 | void | ||
74 | puts(unsigned char *cp) | ||
75 | { | ||
76 | unsigned char ch; | ||
77 | int i = 0; | ||
78 | |||
79 | while (*cp) { | ||
80 | do { | ||
81 | ch = com1[SER_CMD]; | ||
82 | slow_down(); | ||
83 | i++; | ||
84 | if (i>TIMEOUT) { | ||
85 | break; | ||
86 | } | ||
87 | } while (0 == (ch & TX_BUSY)); | ||
88 | com1[SER_DATA] = *cp++; | ||
89 | } | ||
90 | putch('\r'); | ||
91 | putch('\n'); | ||
92 | } | ||
93 | |||
94 | void | ||
95 | fputs(const char *cp) | ||
96 | { | ||
97 | unsigned char ch; | ||
98 | int i = 0; | ||
99 | |||
100 | while (*cp) { | ||
101 | |||
102 | do { | ||
103 | ch = com1[SER_CMD]; | ||
104 | slow_down(); | ||
105 | i++; | ||
106 | if (i>TIMEOUT) { | ||
107 | break; | ||
108 | } | ||
109 | } while (0 == (ch & TX_BUSY)); | ||
110 | com1[SER_DATA] = *cp++; | ||
111 | } | ||
112 | } | ||
113 | |||
114 | |||
115 | void | ||
116 | put64(uint64_t ul) | ||
117 | { | ||
118 | int cnt; | ||
119 | unsigned ch; | ||
120 | |||
121 | cnt = 16; /* 16 nibbles in a 64 bit long */ | ||
122 | putch('0'); | ||
123 | putch('x'); | ||
124 | do { | ||
125 | cnt--; | ||
126 | ch = (unsigned char)(ul >> cnt * 4) & 0x0F; | ||
127 | putch(digits[ch]); | ||
128 | } while (cnt > 0); | ||
129 | } | ||
130 | |||
131 | void | ||
132 | put32(unsigned u) | ||
133 | { | ||
134 | int cnt; | ||
135 | unsigned ch; | ||
136 | |||
137 | cnt = 8; /* 8 nibbles in a 32 bit long */ | ||
138 | putch('0'); | ||
139 | putch('x'); | ||
140 | do { | ||
141 | cnt--; | ||
142 | ch = (unsigned char)(u >> cnt * 4) & 0x0F; | ||
143 | putch(digits[ch]); | ||
144 | } while (cnt > 0); | ||
145 | } | ||
diff --git a/arch/mips/au1000/common/reset.c b/arch/mips/au1000/common/reset.c new file mode 100644 index 000000000000..65b84db800e4 --- /dev/null +++ b/arch/mips/au1000/common/reset.c | |||
@@ -0,0 +1,195 @@ | |||
1 | /* | ||
2 | * | ||
3 | * BRIEF MODULE DESCRIPTION | ||
4 | * Au1000 reset routines. | ||
5 | * | ||
6 | * Copyright 2001 MontaVista Software Inc. | ||
7 | * Author: MontaVista Software, Inc. | ||
8 | * ppopov@mvista.com or source@mvista.com | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | * | ||
15 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
16 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
17 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
18 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
21 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
22 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
25 | * | ||
26 | * You should have received a copy of the GNU General Public License along | ||
27 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
28 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
29 | */ | ||
30 | #include <linux/config.h> | ||
31 | #include <linux/sched.h> | ||
32 | #include <linux/mm.h> | ||
33 | #include <asm/io.h> | ||
34 | #include <asm/pgtable.h> | ||
35 | #include <asm/processor.h> | ||
36 | #include <asm/reboot.h> | ||
37 | #include <asm/system.h> | ||
38 | #include <asm/mach-au1x00/au1000.h> | ||
39 | |||
40 | extern int au_sleep(void); | ||
41 | extern void (*flush_cache_all)(void); | ||
42 | |||
43 | void au1000_restart(char *command) | ||
44 | { | ||
45 | /* Set all integrated peripherals to disabled states */ | ||
46 | extern void board_reset (void); | ||
47 | u32 prid = read_c0_prid(); | ||
48 | |||
49 | printk(KERN_NOTICE "\n** Resetting Integrated Peripherals\n"); | ||
50 | switch (prid & 0xFF000000) | ||
51 | { | ||
52 | case 0x00000000: /* Au1000 */ | ||
53 | au_writel(0x02, 0xb0000010); /* ac97_enable */ | ||
54 | au_writel(0x08, 0xb017fffc); /* usbh_enable - early errata */ | ||
55 | asm("sync"); | ||
56 | au_writel(0x00, 0xb017fffc); /* usbh_enable */ | ||
57 | au_writel(0x00, 0xb0200058); /* usbd_enable */ | ||
58 | au_writel(0x00, 0xb0300040); /* ir_enable */ | ||
59 | au_writel(0x00, 0xb4004104); /* mac dma */ | ||
60 | au_writel(0x00, 0xb4004114); /* mac dma */ | ||
61 | au_writel(0x00, 0xb4004124); /* mac dma */ | ||
62 | au_writel(0x00, 0xb4004134); /* mac dma */ | ||
63 | au_writel(0x00, 0xb0520000); /* macen0 */ | ||
64 | au_writel(0x00, 0xb0520004); /* macen1 */ | ||
65 | au_writel(0x00, 0xb1000008); /* i2s_enable */ | ||
66 | au_writel(0x00, 0xb1100100); /* uart0_enable */ | ||
67 | au_writel(0x00, 0xb1200100); /* uart1_enable */ | ||
68 | au_writel(0x00, 0xb1300100); /* uart2_enable */ | ||
69 | au_writel(0x00, 0xb1400100); /* uart3_enable */ | ||
70 | au_writel(0x02, 0xb1600100); /* ssi0_enable */ | ||
71 | au_writel(0x02, 0xb1680100); /* ssi1_enable */ | ||
72 | au_writel(0x00, 0xb1900020); /* sys_freqctrl0 */ | ||
73 | au_writel(0x00, 0xb1900024); /* sys_freqctrl1 */ | ||
74 | au_writel(0x00, 0xb1900028); /* sys_clksrc */ | ||
75 | au_writel(0x10, 0xb1900060); /* sys_cpupll */ | ||
76 | au_writel(0x00, 0xb1900064); /* sys_auxpll */ | ||
77 | au_writel(0x00, 0xb1900100); /* sys_pininputen */ | ||
78 | break; | ||
79 | case 0x01000000: /* Au1500 */ | ||
80 | au_writel(0x02, 0xb0000010); /* ac97_enable */ | ||
81 | au_writel(0x08, 0xb017fffc); /* usbh_enable - early errata */ | ||
82 | asm("sync"); | ||
83 | au_writel(0x00, 0xb017fffc); /* usbh_enable */ | ||
84 | au_writel(0x00, 0xb0200058); /* usbd_enable */ | ||
85 | au_writel(0x00, 0xb4004104); /* mac dma */ | ||
86 | au_writel(0x00, 0xb4004114); /* mac dma */ | ||
87 | au_writel(0x00, 0xb4004124); /* mac dma */ | ||
88 | au_writel(0x00, 0xb4004134); /* mac dma */ | ||
89 | au_writel(0x00, 0xb1520000); /* macen0 */ | ||
90 | au_writel(0x00, 0xb1520004); /* macen1 */ | ||
91 | au_writel(0x00, 0xb1100100); /* uart0_enable */ | ||
92 | au_writel(0x00, 0xb1400100); /* uart3_enable */ | ||
93 | au_writel(0x00, 0xb1900020); /* sys_freqctrl0 */ | ||
94 | au_writel(0x00, 0xb1900024); /* sys_freqctrl1 */ | ||
95 | au_writel(0x00, 0xb1900028); /* sys_clksrc */ | ||
96 | au_writel(0x10, 0xb1900060); /* sys_cpupll */ | ||
97 | au_writel(0x00, 0xb1900064); /* sys_auxpll */ | ||
98 | au_writel(0x00, 0xb1900100); /* sys_pininputen */ | ||
99 | break; | ||
100 | case 0x02000000: /* Au1100 */ | ||
101 | au_writel(0x02, 0xb0000010); /* ac97_enable */ | ||
102 | au_writel(0x08, 0xb017fffc); /* usbh_enable - early errata */ | ||
103 | asm("sync"); | ||
104 | au_writel(0x00, 0xb017fffc); /* usbh_enable */ | ||
105 | au_writel(0x00, 0xb0200058); /* usbd_enable */ | ||
106 | au_writel(0x00, 0xb0300040); /* ir_enable */ | ||
107 | au_writel(0x00, 0xb4004104); /* mac dma */ | ||
108 | au_writel(0x00, 0xb4004114); /* mac dma */ | ||
109 | au_writel(0x00, 0xb4004124); /* mac dma */ | ||
110 | au_writel(0x00, 0xb4004134); /* mac dma */ | ||
111 | au_writel(0x00, 0xb0520000); /* macen0 */ | ||
112 | au_writel(0x00, 0xb1000008); /* i2s_enable */ | ||
113 | au_writel(0x00, 0xb1100100); /* uart0_enable */ | ||
114 | au_writel(0x00, 0xb1200100); /* uart1_enable */ | ||
115 | au_writel(0x00, 0xb1400100); /* uart3_enable */ | ||
116 | au_writel(0x02, 0xb1600100); /* ssi0_enable */ | ||
117 | au_writel(0x02, 0xb1680100); /* ssi1_enable */ | ||
118 | au_writel(0x00, 0xb1900020); /* sys_freqctrl0 */ | ||
119 | au_writel(0x00, 0xb1900024); /* sys_freqctrl1 */ | ||
120 | au_writel(0x00, 0xb1900028); /* sys_clksrc */ | ||
121 | au_writel(0x10, 0xb1900060); /* sys_cpupll */ | ||
122 | au_writel(0x00, 0xb1900064); /* sys_auxpll */ | ||
123 | au_writel(0x00, 0xb1900100); /* sys_pininputen */ | ||
124 | break; | ||
125 | case 0x03000000: /* Au1550 */ | ||
126 | au_writel(0x00, 0xb1a00004); /* psc 0 */ | ||
127 | au_writel(0x00, 0xb1b00004); /* psc 1 */ | ||
128 | au_writel(0x00, 0xb0a00004); /* psc 2 */ | ||
129 | au_writel(0x00, 0xb0b00004); /* psc 3 */ | ||
130 | au_writel(0x00, 0xb017fffc); /* usbh_enable */ | ||
131 | au_writel(0x00, 0xb0200058); /* usbd_enable */ | ||
132 | au_writel(0x00, 0xb4004104); /* mac dma */ | ||
133 | au_writel(0x00, 0xb4004114); /* mac dma */ | ||
134 | au_writel(0x00, 0xb4004124); /* mac dma */ | ||
135 | au_writel(0x00, 0xb4004134); /* mac dma */ | ||
136 | au_writel(0x00, 0xb1520000); /* macen0 */ | ||
137 | au_writel(0x00, 0xb1520004); /* macen1 */ | ||
138 | au_writel(0x00, 0xb1100100); /* uart0_enable */ | ||
139 | au_writel(0x00, 0xb1200100); /* uart1_enable */ | ||
140 | au_writel(0x00, 0xb1400100); /* uart3_enable */ | ||
141 | au_writel(0x00, 0xb1900020); /* sys_freqctrl0 */ | ||
142 | au_writel(0x00, 0xb1900024); /* sys_freqctrl1 */ | ||
143 | au_writel(0x00, 0xb1900028); /* sys_clksrc */ | ||
144 | au_writel(0x10, 0xb1900060); /* sys_cpupll */ | ||
145 | au_writel(0x00, 0xb1900064); /* sys_auxpll */ | ||
146 | au_writel(0x00, 0xb1900100); /* sys_pininputen */ | ||
147 | break; | ||
148 | |||
149 | default: | ||
150 | break; | ||
151 | } | ||
152 | |||
153 | set_c0_status(ST0_BEV | ST0_ERL); | ||
154 | set_c0_config(CONF_CM_UNCACHED); | ||
155 | flush_cache_all(); | ||
156 | write_c0_wired(0); | ||
157 | |||
158 | /* Give board a chance to do a hardware reset */ | ||
159 | board_reset(); | ||
160 | |||
161 | /* Jump to the beggining in case board_reset() is empty */ | ||
162 | __asm__ __volatile__("jr\t%0"::"r"(0xbfc00000)); | ||
163 | } | ||
164 | |||
165 | void au1000_halt(void) | ||
166 | { | ||
167 | #if defined(CONFIG_MIPS_PB1550) | ||
168 | /* power off system */ | ||
169 | printk("\n** Powering off Pb1550\n"); | ||
170 | au_writew(au_readw(0xAF00001C) | (3<<14), 0xAF00001C); | ||
171 | au_sync(); | ||
172 | while(1); /* should not get here */ | ||
173 | #endif | ||
174 | printk(KERN_NOTICE "\n** You can safely turn off the power\n"); | ||
175 | #ifdef CONFIG_MIPS_MIRAGE | ||
176 | au_writel((1 << 26) | (1 << 10), GPIO2_OUTPUT); | ||
177 | #endif | ||
178 | #ifdef CONFIG_PM | ||
179 | au_sleep(); | ||
180 | |||
181 | /* should not get here */ | ||
182 | printk(KERN_ERR "Unable to put cpu in sleep mode\n"); | ||
183 | while(1); | ||
184 | #else | ||
185 | while (1) | ||
186 | __asm__(".set\tmips3\n\t" | ||
187 | "wait\n\t" | ||
188 | ".set\tmips0"); | ||
189 | #endif | ||
190 | } | ||
191 | |||
192 | void au1000_power_off(void) | ||
193 | { | ||
194 | au1000_halt(); | ||
195 | } | ||
diff --git a/arch/mips/au1000/common/setup.c b/arch/mips/au1000/common/setup.c new file mode 100644 index 000000000000..dbc8b1bda963 --- /dev/null +++ b/arch/mips/au1000/common/setup.c | |||
@@ -0,0 +1,195 @@ | |||
1 | /* | ||
2 | * Copyright 2000 MontaVista Software Inc. | ||
3 | * Author: MontaVista Software, Inc. | ||
4 | * ppopov@mvista.com or source@mvista.com | ||
5 | * | ||
6 | * Updates to 2.6, Pete Popov, Embedded Alley Solutions, Inc. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | * | ||
13 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
14 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
15 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
16 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
19 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
20 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License along | ||
25 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
26 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
27 | */ | ||
28 | #include <linux/config.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <linux/sched.h> | ||
31 | #include <linux/ioport.h> | ||
32 | #include <linux/mm.h> | ||
33 | #include <linux/delay.h> | ||
34 | #include <linux/interrupt.h> | ||
35 | |||
36 | #include <asm/cpu.h> | ||
37 | #include <asm/bootinfo.h> | ||
38 | #include <asm/irq.h> | ||
39 | #include <asm/mipsregs.h> | ||
40 | #include <asm/reboot.h> | ||
41 | #include <asm/pgtable.h> | ||
42 | #include <asm/mach-au1x00/au1000.h> | ||
43 | #include <asm/time.h> | ||
44 | |||
45 | extern char * __init prom_getcmdline(void); | ||
46 | extern void __init board_setup(void); | ||
47 | extern void au1000_restart(char *); | ||
48 | extern void au1000_halt(void); | ||
49 | extern void au1000_power_off(void); | ||
50 | extern struct resource ioport_resource; | ||
51 | extern struct resource iomem_resource; | ||
52 | extern void (*board_time_init)(void); | ||
53 | extern void au1x_time_init(void); | ||
54 | extern void (*board_timer_setup)(struct irqaction *irq); | ||
55 | extern void au1x_timer_setup(struct irqaction *irq); | ||
56 | extern void au1xxx_time_init(void); | ||
57 | extern void au1xxx_timer_setup(struct irqaction *irq); | ||
58 | extern void set_cpuspec(void); | ||
59 | |||
60 | static int __init au1x00_setup(void) | ||
61 | { | ||
62 | struct cpu_spec *sp; | ||
63 | char *argptr; | ||
64 | unsigned long prid, cpupll, bclk = 1; | ||
65 | |||
66 | set_cpuspec(); | ||
67 | sp = cur_cpu_spec[0]; | ||
68 | |||
69 | board_setup(); /* board specific setup */ | ||
70 | |||
71 | prid = read_c0_prid(); | ||
72 | cpupll = (au_readl(0xB1900060) & 0x3F) * 12; | ||
73 | printk("(PRId %08lx) @ %ldMHZ\n", prid, cpupll); | ||
74 | |||
75 | bclk = sp->cpu_bclk; | ||
76 | if (bclk) | ||
77 | { | ||
78 | /* Enable BCLK switching */ | ||
79 | bclk = au_readl(0xB190003C); | ||
80 | au_writel(bclk | 0x60, 0xB190003C); | ||
81 | printk("BCLK switching enabled!\n"); | ||
82 | } | ||
83 | |||
84 | if (sp->cpu_od) { | ||
85 | /* Various early Au1000 Errata corrected by this */ | ||
86 | set_c0_config(1<<19); /* Set Config[OD] */ | ||
87 | } | ||
88 | else { | ||
89 | /* Clear to obtain best system bus performance */ | ||
90 | clear_c0_config(1<<19); /* Clear Config[OD] */ | ||
91 | } | ||
92 | |||
93 | argptr = prom_getcmdline(); | ||
94 | |||
95 | #ifdef CONFIG_SERIAL_AU1X00_CONSOLE | ||
96 | if ((argptr = strstr(argptr, "console=")) == NULL) { | ||
97 | argptr = prom_getcmdline(); | ||
98 | strcat(argptr, " console=ttyS0,115200"); | ||
99 | } | ||
100 | #endif | ||
101 | |||
102 | #ifdef CONFIG_FB_AU1100 | ||
103 | if ((argptr = strstr(argptr, "video=")) == NULL) { | ||
104 | argptr = prom_getcmdline(); | ||
105 | /* default panel */ | ||
106 | /*strcat(argptr, " video=au1100fb:panel:Sharp_320x240_16");*/ | ||
107 | #ifdef CONFIG_MIPS_HYDROGEN3 | ||
108 | strcat(argptr, " video=au1100fb:panel:Hydrogen_3_NEC_panel_320x240,nohwcursor"); | ||
109 | #else | ||
110 | strcat(argptr, " video=au1100fb:panel:s10,nohwcursor"); | ||
111 | #endif | ||
112 | } | ||
113 | #endif | ||
114 | |||
115 | #ifdef CONFIG_FB_E1356 | ||
116 | if ((argptr = strstr(argptr, "video=")) == NULL) { | ||
117 | argptr = prom_getcmdline(); | ||
118 | #ifdef CONFIG_MIPS_PB1000 | ||
119 | strcat(argptr, " video=e1356fb:system:pb1000,mmunalign:1"); | ||
120 | #else | ||
121 | strcat(argptr, " video=e1356fb:system:pb1500"); | ||
122 | #endif | ||
123 | } | ||
124 | #endif | ||
125 | |||
126 | #ifdef CONFIG_FB_XPERT98 | ||
127 | if ((argptr = strstr(argptr, "video=")) == NULL) { | ||
128 | argptr = prom_getcmdline(); | ||
129 | strcat(argptr, " video=atyfb:1024x768-8@70"); | ||
130 | } | ||
131 | #endif | ||
132 | |||
133 | #if defined(CONFIG_SOUND_AU1X00) && !defined(CONFIG_SOC_AU1000) | ||
134 | /* au1000 does not support vra, au1500 and au1100 do */ | ||
135 | strcat(argptr, " au1000_audio=vra"); | ||
136 | argptr = prom_getcmdline(); | ||
137 | #endif | ||
138 | _machine_restart = au1000_restart; | ||
139 | _machine_halt = au1000_halt; | ||
140 | _machine_power_off = au1000_power_off; | ||
141 | board_time_init = au1xxx_time_init; | ||
142 | board_timer_setup = au1xxx_timer_setup; | ||
143 | |||
144 | /* IO/MEM resources. */ | ||
145 | set_io_port_base(0); | ||
146 | ioport_resource.start = IOPORT_RESOURCE_START; | ||
147 | ioport_resource.end = IOPORT_RESOURCE_END; | ||
148 | iomem_resource.start = IOMEM_RESOURCE_START; | ||
149 | iomem_resource.end = IOMEM_RESOURCE_END; | ||
150 | |||
151 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_E0S); | ||
152 | au_writel(SYS_CNTRL_E0 | SYS_CNTRL_EN0, SYS_COUNTER_CNTRL); | ||
153 | au_sync(); | ||
154 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T0S); | ||
155 | au_writel(0, SYS_TOYTRIM); | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | early_initcall(au1x00_setup); | ||
161 | |||
162 | #if defined(CONFIG_64BIT_PHYS_ADDR) | ||
163 | /* This routine should be valid for all Au1x based boards */ | ||
164 | phys_t fixup_bigphys_addr(phys_t phys_addr, phys_t size) | ||
165 | { | ||
166 | u32 start, end; | ||
167 | |||
168 | /* Don't fixup 36 bit addresses */ | ||
169 | if ((phys_addr >> 32) != 0) return phys_addr; | ||
170 | |||
171 | #ifdef CONFIG_PCI | ||
172 | start = (u32)Au1500_PCI_MEM_START; | ||
173 | end = (u32)Au1500_PCI_MEM_END; | ||
174 | /* check for pci memory window */ | ||
175 | if ((phys_addr >= start) && ((phys_addr + size) < end)) { | ||
176 | return (phys_t)((phys_addr - start) + Au1500_PCI_MEM_START); | ||
177 | } | ||
178 | #endif | ||
179 | |||
180 | /* All Au1x SOCs have a pcmcia controller */ | ||
181 | /* We setup our 32 bit pseudo addresses to be equal to the | ||
182 | * 36 bit addr >> 4, to make it easier to check the address | ||
183 | * and fix it. | ||
184 | * The Au1x socket 0 phys attribute address is 0xF 4000 0000. | ||
185 | * The pseudo address we use is 0xF400 0000. Any address over | ||
186 | * 0xF400 0000 is a pcmcia pseudo address. | ||
187 | */ | ||
188 | if ((phys_addr >= 0xF4000000) && (phys_addr < 0xFFFFFFFF)) { | ||
189 | return (phys_t)(phys_addr << 4); | ||
190 | } | ||
191 | |||
192 | /* default nop */ | ||
193 | return phys_addr; | ||
194 | } | ||
195 | #endif | ||
diff --git a/arch/mips/au1000/common/sleeper.S b/arch/mips/au1000/common/sleeper.S new file mode 100644 index 000000000000..44dac3b0df3b --- /dev/null +++ b/arch/mips/au1000/common/sleeper.S | |||
@@ -0,0 +1,149 @@ | |||
1 | /* | ||
2 | * Copyright 2002 Embedded Edge, LLC | ||
3 | * Author: dan@embeddededge.com | ||
4 | * | ||
5 | * Sleep helper for Au1xxx sleep mode. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | */ | ||
12 | #include <asm/asm.h> | ||
13 | #include <asm/mipsregs.h> | ||
14 | #include <asm/addrspace.h> | ||
15 | #include <asm/regdef.h> | ||
16 | #include <asm/stackframe.h> | ||
17 | |||
18 | .text | ||
19 | .set macro | ||
20 | .set noat | ||
21 | .align 5 | ||
22 | |||
23 | /* Save all of the processor general registers and go to sleep. | ||
24 | * A wakeup condition will get us back here to restore the registers. | ||
25 | */ | ||
26 | LEAF(save_and_sleep) | ||
27 | |||
28 | subu sp, PT_SIZE | ||
29 | sw $1, PT_R1(sp) | ||
30 | sw $2, PT_R2(sp) | ||
31 | sw $3, PT_R3(sp) | ||
32 | sw $4, PT_R4(sp) | ||
33 | sw $5, PT_R5(sp) | ||
34 | sw $6, PT_R6(sp) | ||
35 | sw $7, PT_R7(sp) | ||
36 | sw $8, PT_R8(sp) | ||
37 | sw $9, PT_R9(sp) | ||
38 | sw $10, PT_R10(sp) | ||
39 | sw $11, PT_R11(sp) | ||
40 | sw $12, PT_R12(sp) | ||
41 | sw $13, PT_R13(sp) | ||
42 | sw $14, PT_R14(sp) | ||
43 | sw $15, PT_R15(sp) | ||
44 | sw $16, PT_R16(sp) | ||
45 | sw $17, PT_R17(sp) | ||
46 | sw $18, PT_R18(sp) | ||
47 | sw $19, PT_R19(sp) | ||
48 | sw $20, PT_R20(sp) | ||
49 | sw $21, PT_R21(sp) | ||
50 | sw $22, PT_R22(sp) | ||
51 | sw $23, PT_R23(sp) | ||
52 | sw $24, PT_R24(sp) | ||
53 | sw $25, PT_R25(sp) | ||
54 | sw $26, PT_R26(sp) | ||
55 | sw $27, PT_R27(sp) | ||
56 | sw $28, PT_R28(sp) | ||
57 | sw $29, PT_R29(sp) | ||
58 | sw $30, PT_R30(sp) | ||
59 | sw $31, PT_R31(sp) | ||
60 | mfc0 k0, CP0_STATUS | ||
61 | sw k0, 0x20(sp) | ||
62 | mfc0 k0, CP0_CONTEXT | ||
63 | sw k0, 0x1c(sp) | ||
64 | mfc0 k0, CP0_PAGEMASK | ||
65 | sw k0, 0x18(sp) | ||
66 | mfc0 k0, CP0_CONFIG | ||
67 | sw k0, 0x14(sp) | ||
68 | |||
69 | /* Now set up the scratch registers so the boot rom will | ||
70 | * return to this point upon wakeup. | ||
71 | */ | ||
72 | la k0, 1f | ||
73 | lui k1, 0xb190 | ||
74 | ori k1, 0x18 | ||
75 | sw sp, 0(k1) | ||
76 | ori k1, 0x1c | ||
77 | sw k0, 0(k1) | ||
78 | |||
79 | /* Put SDRAM into self refresh. Preload instructions into cache, | ||
80 | * issue a precharge, then auto refresh, then sleep commands to it. | ||
81 | */ | ||
82 | la t0, sdsleep | ||
83 | .set mips3 | ||
84 | cache 0x14, 0(t0) | ||
85 | cache 0x14, 32(t0) | ||
86 | cache 0x14, 64(t0) | ||
87 | cache 0x14, 96(t0) | ||
88 | .set mips0 | ||
89 | |||
90 | sdsleep: | ||
91 | lui k0, 0xb400 | ||
92 | sw zero, 0x001c(k0) /* Precharge */ | ||
93 | sw zero, 0x0020(k0) /* Auto refresh */ | ||
94 | sw zero, 0x0030(k0) /* SDRAM sleep */ | ||
95 | sync | ||
96 | |||
97 | lui k1, 0xb190 | ||
98 | sw zero, 0x0078(k1) /* get ready to sleep */ | ||
99 | sync | ||
100 | sw zero, 0x007c(k1) /* Put processor to sleep */ | ||
101 | sync | ||
102 | |||
103 | /* This is where we return upon wakeup. | ||
104 | * Reload all of the registers and return. | ||
105 | */ | ||
106 | 1: nop | ||
107 | lw k0, 0x20(sp) | ||
108 | mtc0 k0, CP0_STATUS | ||
109 | lw k0, 0x1c(sp) | ||
110 | mtc0 k0, CP0_CONTEXT | ||
111 | lw k0, 0x18(sp) | ||
112 | mtc0 k0, CP0_PAGEMASK | ||
113 | lw k0, 0x14(sp) | ||
114 | mtc0 k0, CP0_CONFIG | ||
115 | lw $1, PT_R1(sp) | ||
116 | lw $2, PT_R2(sp) | ||
117 | lw $3, PT_R3(sp) | ||
118 | lw $4, PT_R4(sp) | ||
119 | lw $5, PT_R5(sp) | ||
120 | lw $6, PT_R6(sp) | ||
121 | lw $7, PT_R7(sp) | ||
122 | lw $8, PT_R8(sp) | ||
123 | lw $9, PT_R9(sp) | ||
124 | lw $10, PT_R10(sp) | ||
125 | lw $11, PT_R11(sp) | ||
126 | lw $12, PT_R12(sp) | ||
127 | lw $13, PT_R13(sp) | ||
128 | lw $14, PT_R14(sp) | ||
129 | lw $15, PT_R15(sp) | ||
130 | lw $16, PT_R16(sp) | ||
131 | lw $17, PT_R17(sp) | ||
132 | lw $18, PT_R18(sp) | ||
133 | lw $19, PT_R19(sp) | ||
134 | lw $20, PT_R20(sp) | ||
135 | lw $21, PT_R21(sp) | ||
136 | lw $22, PT_R22(sp) | ||
137 | lw $23, PT_R23(sp) | ||
138 | lw $24, PT_R24(sp) | ||
139 | lw $25, PT_R25(sp) | ||
140 | lw $26, PT_R26(sp) | ||
141 | lw $27, PT_R27(sp) | ||
142 | lw $28, PT_R28(sp) | ||
143 | lw $29, PT_R29(sp) | ||
144 | lw $30, PT_R30(sp) | ||
145 | lw $31, PT_R31(sp) | ||
146 | addiu sp, PT_SIZE | ||
147 | |||
148 | jr ra | ||
149 | END(save_and_sleep) | ||
diff --git a/arch/mips/au1000/common/time.c b/arch/mips/au1000/common/time.c new file mode 100644 index 000000000000..fe418f1620c3 --- /dev/null +++ b/arch/mips/au1000/common/time.c | |||
@@ -0,0 +1,469 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Copyright (C) 2001 MontaVista Software, ppopov@mvista.com | ||
4 | * Copied and modified Carsten Langgaard's time.c | ||
5 | * | ||
6 | * Carsten Langgaard, carstenl@mips.com | ||
7 | * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. | ||
8 | * | ||
9 | * ######################################################################## | ||
10 | * | ||
11 | * This program is free software; you can distribute it and/or modify it | ||
12 | * under the terms of the GNU General Public License (Version 2) as | ||
13 | * published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
17 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
18 | * for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License along | ||
21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
22 | * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
23 | * | ||
24 | * ######################################################################## | ||
25 | * | ||
26 | * Setting up the clock on the MIPS boards. | ||
27 | * | ||
28 | * Update. Always configure the kernel with CONFIG_NEW_TIME_C. This | ||
29 | * will use the user interface gettimeofday() functions from the | ||
30 | * arch/mips/kernel/time.c, and we provide the clock interrupt processing | ||
31 | * and the timer offset compute functions. If CONFIG_PM is selected, | ||
32 | * we also ensure the 32KHz timer is available. -- Dan | ||
33 | */ | ||
34 | |||
35 | #include <linux/types.h> | ||
36 | #include <linux/config.h> | ||
37 | #include <linux/init.h> | ||
38 | #include <linux/kernel_stat.h> | ||
39 | #include <linux/sched.h> | ||
40 | #include <linux/spinlock.h> | ||
41 | #include <linux/hardirq.h> | ||
42 | |||
43 | #include <asm/compiler.h> | ||
44 | #include <asm/mipsregs.h> | ||
45 | #include <asm/ptrace.h> | ||
46 | #include <asm/time.h> | ||
47 | #include <asm/div64.h> | ||
48 | #include <asm/mach-au1x00/au1000.h> | ||
49 | |||
50 | #include <linux/mc146818rtc.h> | ||
51 | #include <linux/timex.h> | ||
52 | |||
53 | extern void startup_match20_interrupt(void); | ||
54 | extern void do_softirq(void); | ||
55 | extern volatile unsigned long wall_jiffies; | ||
56 | unsigned long missed_heart_beats = 0; | ||
57 | |||
58 | static unsigned long r4k_offset; /* Amount to increment compare reg each time */ | ||
59 | static unsigned long r4k_cur; /* What counter should be at next timer irq */ | ||
60 | int no_au1xxx_32khz; | ||
61 | void (*au1k_wait_ptr)(void); | ||
62 | |||
63 | /* Cycle counter value at the previous timer interrupt.. */ | ||
64 | static unsigned int timerhi = 0, timerlo = 0; | ||
65 | |||
66 | #ifdef CONFIG_PM | ||
67 | #define MATCH20_INC 328 | ||
68 | extern void startup_match20_interrupt(void); | ||
69 | static unsigned long last_pc0, last_match20; | ||
70 | #endif | ||
71 | |||
72 | static DEFINE_SPINLOCK(time_lock); | ||
73 | |||
74 | static inline void ack_r4ktimer(unsigned long newval) | ||
75 | { | ||
76 | write_c0_compare(newval); | ||
77 | } | ||
78 | |||
79 | /* | ||
80 | * There are a lot of conceptually broken versions of the MIPS timer interrupt | ||
81 | * handler floating around. This one is rather different, but the algorithm | ||
82 | * is provably more robust. | ||
83 | */ | ||
84 | unsigned long wtimer; | ||
85 | void mips_timer_interrupt(struct pt_regs *regs) | ||
86 | { | ||
87 | int irq = 63; | ||
88 | unsigned long count; | ||
89 | |||
90 | irq_enter(); | ||
91 | kstat_this_cpu.irqs[irq]++; | ||
92 | |||
93 | if (r4k_offset == 0) | ||
94 | goto null; | ||
95 | |||
96 | do { | ||
97 | count = read_c0_count(); | ||
98 | timerhi += (count < timerlo); /* Wrap around */ | ||
99 | timerlo = count; | ||
100 | |||
101 | kstat_this_cpu.irqs[irq]++; | ||
102 | do_timer(regs); | ||
103 | #ifndef CONFIG_SMP | ||
104 | update_process_times(user_mode(regs)); | ||
105 | #endif | ||
106 | r4k_cur += r4k_offset; | ||
107 | ack_r4ktimer(r4k_cur); | ||
108 | |||
109 | } while (((unsigned long)read_c0_count() | ||
110 | - r4k_cur) < 0x7fffffff); | ||
111 | |||
112 | irq_exit(); | ||
113 | return; | ||
114 | |||
115 | null: | ||
116 | ack_r4ktimer(0); | ||
117 | } | ||
118 | |||
119 | #ifdef CONFIG_PM | ||
120 | void counter0_irq(int irq, void *dev_id, struct pt_regs *regs) | ||
121 | { | ||
122 | unsigned long pc0; | ||
123 | int time_elapsed; | ||
124 | static int jiffie_drift = 0; | ||
125 | |||
126 | kstat.irqs[0][irq]++; | ||
127 | if (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20) { | ||
128 | /* should never happen! */ | ||
129 | printk(KERN_WARNING "counter 0 w status eror\n"); | ||
130 | return; | ||
131 | } | ||
132 | |||
133 | pc0 = au_readl(SYS_TOYREAD); | ||
134 | if (pc0 < last_match20) { | ||
135 | /* counter overflowed */ | ||
136 | time_elapsed = (0xffffffff - last_match20) + pc0; | ||
137 | } | ||
138 | else { | ||
139 | time_elapsed = pc0 - last_match20; | ||
140 | } | ||
141 | |||
142 | while (time_elapsed > 0) { | ||
143 | do_timer(regs); | ||
144 | #ifndef CONFIG_SMP | ||
145 | update_process_times(user_mode(regs)); | ||
146 | #endif | ||
147 | time_elapsed -= MATCH20_INC; | ||
148 | last_match20 += MATCH20_INC; | ||
149 | jiffie_drift++; | ||
150 | } | ||
151 | |||
152 | last_pc0 = pc0; | ||
153 | au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2); | ||
154 | au_sync(); | ||
155 | |||
156 | /* our counter ticks at 10.009765625 ms/tick, we we're running | ||
157 | * almost 10uS too slow per tick. | ||
158 | */ | ||
159 | |||
160 | if (jiffie_drift >= 999) { | ||
161 | jiffie_drift -= 999; | ||
162 | do_timer(regs); /* increment jiffies by one */ | ||
163 | #ifndef CONFIG_SMP | ||
164 | update_process_times(user_mode(regs)); | ||
165 | #endif | ||
166 | } | ||
167 | } | ||
168 | |||
169 | /* When we wakeup from sleep, we have to "catch up" on all of the | ||
170 | * timer ticks we have missed. | ||
171 | */ | ||
172 | void | ||
173 | wakeup_counter0_adjust(void) | ||
174 | { | ||
175 | unsigned long pc0; | ||
176 | int time_elapsed; | ||
177 | |||
178 | pc0 = au_readl(SYS_TOYREAD); | ||
179 | if (pc0 < last_match20) { | ||
180 | /* counter overflowed */ | ||
181 | time_elapsed = (0xffffffff - last_match20) + pc0; | ||
182 | } | ||
183 | else { | ||
184 | time_elapsed = pc0 - last_match20; | ||
185 | } | ||
186 | |||
187 | while (time_elapsed > 0) { | ||
188 | time_elapsed -= MATCH20_INC; | ||
189 | last_match20 += MATCH20_INC; | ||
190 | } | ||
191 | |||
192 | last_pc0 = pc0; | ||
193 | au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2); | ||
194 | au_sync(); | ||
195 | |||
196 | } | ||
197 | |||
198 | /* This is just for debugging to set the timer for a sleep delay. | ||
199 | */ | ||
200 | void | ||
201 | wakeup_counter0_set(int ticks) | ||
202 | { | ||
203 | unsigned long pc0; | ||
204 | |||
205 | pc0 = au_readl(SYS_TOYREAD); | ||
206 | last_pc0 = pc0; | ||
207 | au_writel(last_match20 + (MATCH20_INC * ticks), SYS_TOYMATCH2); | ||
208 | au_sync(); | ||
209 | } | ||
210 | #endif | ||
211 | |||
212 | /* I haven't found anyone that doesn't use a 12 MHz source clock, | ||
213 | * but just in case..... | ||
214 | */ | ||
215 | #ifdef CONFIG_AU1000_SRC_CLK | ||
216 | #define AU1000_SRC_CLK CONFIG_AU1000_SRC_CLK | ||
217 | #else | ||
218 | #define AU1000_SRC_CLK 12000000 | ||
219 | #endif | ||
220 | |||
221 | /* | ||
222 | * We read the real processor speed from the PLL. This is important | ||
223 | * because it is more accurate than computing it from the 32KHz | ||
224 | * counter, if it exists. If we don't have an accurate processor | ||
225 | * speed, all of the peripherals that derive their clocks based on | ||
226 | * this advertised speed will introduce error and sometimes not work | ||
227 | * properly. This function is futher convoluted to still allow configurations | ||
228 | * to do that in case they have really, really old silicon with a | ||
229 | * write-only PLL register, that we need the 32KHz when power management | ||
230 | * "wait" is enabled, and we need to detect if the 32KHz isn't present | ||
231 | * but requested......got it? :-) -- Dan | ||
232 | */ | ||
233 | unsigned long cal_r4koff(void) | ||
234 | { | ||
235 | unsigned long count; | ||
236 | unsigned long cpu_speed; | ||
237 | unsigned long flags; | ||
238 | unsigned long counter; | ||
239 | |||
240 | spin_lock_irqsave(&time_lock, flags); | ||
241 | |||
242 | /* Power management cares if we don't have a 32KHz counter. | ||
243 | */ | ||
244 | no_au1xxx_32khz = 0; | ||
245 | counter = au_readl(SYS_COUNTER_CNTRL); | ||
246 | if (counter & SYS_CNTRL_E0) { | ||
247 | int trim_divide = 16; | ||
248 | |||
249 | au_writel(counter | SYS_CNTRL_EN1, SYS_COUNTER_CNTRL); | ||
250 | |||
251 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S); | ||
252 | /* RTC now ticks at 32.768/16 kHz */ | ||
253 | au_writel(trim_divide-1, SYS_RTCTRIM); | ||
254 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S); | ||
255 | |||
256 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S); | ||
257 | au_writel (0, SYS_TOYWRITE); | ||
258 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S); | ||
259 | |||
260 | #if defined(CONFIG_AU1000_USE32K) | ||
261 | { | ||
262 | unsigned long start, end; | ||
263 | |||
264 | start = au_readl(SYS_RTCREAD); | ||
265 | start += 2; | ||
266 | /* wait for the beginning of a new tick | ||
267 | */ | ||
268 | while (au_readl(SYS_RTCREAD) < start); | ||
269 | |||
270 | /* Start r4k counter. | ||
271 | */ | ||
272 | write_c0_count(0); | ||
273 | |||
274 | /* Wait 0.5 seconds. | ||
275 | */ | ||
276 | end = start + (32768 / trim_divide)/2; | ||
277 | |||
278 | while (end > au_readl(SYS_RTCREAD)); | ||
279 | |||
280 | count = read_c0_count(); | ||
281 | cpu_speed = count * 2; | ||
282 | } | ||
283 | #else | ||
284 | cpu_speed = (au_readl(SYS_CPUPLL) & 0x0000003f) * | ||
285 | AU1000_SRC_CLK; | ||
286 | count = cpu_speed / 2; | ||
287 | #endif | ||
288 | } | ||
289 | else { | ||
290 | /* The 32KHz oscillator isn't running, so assume there | ||
291 | * isn't one and grab the processor speed from the PLL. | ||
292 | * NOTE: some old silicon doesn't allow reading the PLL. | ||
293 | */ | ||
294 | cpu_speed = (au_readl(SYS_CPUPLL) & 0x0000003f) * AU1000_SRC_CLK; | ||
295 | count = cpu_speed / 2; | ||
296 | no_au1xxx_32khz = 1; | ||
297 | } | ||
298 | mips_hpt_frequency = count; | ||
299 | // Equation: Baudrate = CPU / (SD * 2 * CLKDIV * 16) | ||
300 | set_au1x00_uart_baud_base(cpu_speed / (2 * ((int)(au_readl(SYS_POWERCTRL)&0x03) + 2) * 16)); | ||
301 | spin_unlock_irqrestore(&time_lock, flags); | ||
302 | return (cpu_speed / HZ); | ||
303 | } | ||
304 | |||
305 | /* This is for machines which generate the exact clock. */ | ||
306 | #define USECS_PER_JIFFY (1000000/HZ) | ||
307 | #define USECS_PER_JIFFY_FRAC (0x100000000LL*1000000/HZ&0xffffffff) | ||
308 | |||
309 | static unsigned long | ||
310 | div64_32(unsigned long v1, unsigned long v2, unsigned long v3) | ||
311 | { | ||
312 | unsigned long r0; | ||
313 | do_div64_32(r0, v1, v2, v3); | ||
314 | return r0; | ||
315 | } | ||
316 | |||
317 | static unsigned long do_fast_cp0_gettimeoffset(void) | ||
318 | { | ||
319 | u32 count; | ||
320 | unsigned long res, tmp; | ||
321 | unsigned long r0; | ||
322 | |||
323 | /* Last jiffy when do_fast_gettimeoffset() was called. */ | ||
324 | static unsigned long last_jiffies=0; | ||
325 | unsigned long quotient; | ||
326 | |||
327 | /* | ||
328 | * Cached "1/(clocks per usec)*2^32" value. | ||
329 | * It has to be recalculated once each jiffy. | ||
330 | */ | ||
331 | static unsigned long cached_quotient=0; | ||
332 | |||
333 | tmp = jiffies; | ||
334 | |||
335 | quotient = cached_quotient; | ||
336 | |||
337 | if (tmp && last_jiffies != tmp) { | ||
338 | last_jiffies = tmp; | ||
339 | if (last_jiffies != 0) { | ||
340 | r0 = div64_32(timerhi, timerlo, tmp); | ||
341 | quotient = div64_32(USECS_PER_JIFFY, USECS_PER_JIFFY_FRAC, r0); | ||
342 | cached_quotient = quotient; | ||
343 | } | ||
344 | } | ||
345 | |||
346 | /* Get last timer tick in absolute kernel time */ | ||
347 | count = read_c0_count(); | ||
348 | |||
349 | /* .. relative to previous jiffy (32 bits is enough) */ | ||
350 | count -= timerlo; | ||
351 | |||
352 | __asm__("multu\t%1,%2\n\t" | ||
353 | "mfhi\t%0" | ||
354 | : "=r" (res) | ||
355 | : "r" (count), "r" (quotient) | ||
356 | : "hi", "lo", GCC_REG_ACCUM); | ||
357 | |||
358 | /* | ||
359 | * Due to possible jiffies inconsistencies, we need to check | ||
360 | * the result so that we'll get a timer that is monotonic. | ||
361 | */ | ||
362 | if (res >= USECS_PER_JIFFY) | ||
363 | res = USECS_PER_JIFFY-1; | ||
364 | |||
365 | return res; | ||
366 | } | ||
367 | |||
368 | #ifdef CONFIG_PM | ||
369 | static unsigned long do_fast_pm_gettimeoffset(void) | ||
370 | { | ||
371 | unsigned long pc0; | ||
372 | unsigned long offset; | ||
373 | |||
374 | pc0 = au_readl(SYS_TOYREAD); | ||
375 | au_sync(); | ||
376 | offset = pc0 - last_pc0; | ||
377 | if (offset > 2*MATCH20_INC) { | ||
378 | printk("huge offset %x, last_pc0 %x last_match20 %x pc0 %x\n", | ||
379 | (unsigned)offset, (unsigned)last_pc0, | ||
380 | (unsigned)last_match20, (unsigned)pc0); | ||
381 | } | ||
382 | offset = (unsigned long)((offset * 305) / 10); | ||
383 | return offset; | ||
384 | } | ||
385 | #endif | ||
386 | |||
387 | void au1xxx_timer_setup(struct irqaction *irq) | ||
388 | { | ||
389 | unsigned int est_freq; | ||
390 | extern unsigned long (*do_gettimeoffset)(void); | ||
391 | extern void au1k_wait(void); | ||
392 | |||
393 | printk("calculating r4koff... "); | ||
394 | r4k_offset = cal_r4koff(); | ||
395 | printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset); | ||
396 | |||
397 | //est_freq = 2*r4k_offset*HZ; | ||
398 | est_freq = r4k_offset*HZ; | ||
399 | est_freq += 5000; /* round */ | ||
400 | est_freq -= est_freq%10000; | ||
401 | printk("CPU frequency %d.%02d MHz\n", est_freq/1000000, | ||
402 | (est_freq%1000000)*100/1000000); | ||
403 | set_au1x00_speed(est_freq); | ||
404 | set_au1x00_lcd_clock(); // program the LCD clock | ||
405 | |||
406 | r4k_cur = (read_c0_count() + r4k_offset); | ||
407 | write_c0_compare(r4k_cur); | ||
408 | |||
409 | #ifdef CONFIG_PM | ||
410 | /* | ||
411 | * setup counter 0, since it keeps ticking after a | ||
412 | * 'wait' instruction has been executed. The CP0 timer and | ||
413 | * counter 1 do NOT continue running after 'wait' | ||
414 | * | ||
415 | * It's too early to call request_irq() here, so we handle | ||
416 | * counter 0 interrupt as a special irq and it doesn't show | ||
417 | * up under /proc/interrupts. | ||
418 | * | ||
419 | * Check to ensure we really have a 32KHz oscillator before | ||
420 | * we do this. | ||
421 | */ | ||
422 | if (no_au1xxx_32khz) { | ||
423 | unsigned int c0_status; | ||
424 | |||
425 | printk("WARNING: no 32KHz clock found.\n"); | ||
426 | do_gettimeoffset = do_fast_cp0_gettimeoffset; | ||
427 | |||
428 | /* Ensure we get CPO_COUNTER interrupts. | ||
429 | */ | ||
430 | c0_status = read_c0_status(); | ||
431 | c0_status |= IE_IRQ5; | ||
432 | write_c0_status(c0_status); | ||
433 | } | ||
434 | else { | ||
435 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S); | ||
436 | au_writel(0, SYS_TOYWRITE); | ||
437 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S); | ||
438 | |||
439 | au_writel(au_readl(SYS_WAKEMSK) | (1<<8), SYS_WAKEMSK); | ||
440 | au_writel(~0, SYS_WAKESRC); | ||
441 | au_sync(); | ||
442 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20); | ||
443 | |||
444 | /* setup match20 to interrupt once every 10ms */ | ||
445 | last_pc0 = last_match20 = au_readl(SYS_TOYREAD); | ||
446 | au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2); | ||
447 | au_sync(); | ||
448 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20); | ||
449 | startup_match20_interrupt(); | ||
450 | |||
451 | do_gettimeoffset = do_fast_pm_gettimeoffset; | ||
452 | |||
453 | /* We can use the real 'wait' instruction. | ||
454 | */ | ||
455 | au1k_wait_ptr = au1k_wait; | ||
456 | } | ||
457 | |||
458 | #else | ||
459 | /* We have to do this here instead of in timer_init because | ||
460 | * the generic code in arch/mips/kernel/time.c will write | ||
461 | * over our function pointer. | ||
462 | */ | ||
463 | do_gettimeoffset = do_fast_cp0_gettimeoffset; | ||
464 | #endif | ||
465 | } | ||
466 | |||
467 | void __init au1xxx_time_init(void) | ||
468 | { | ||
469 | } | ||
diff --git a/arch/mips/au1000/common/usbdev.c b/arch/mips/au1000/common/usbdev.c new file mode 100644 index 000000000000..447a9a4612a8 --- /dev/null +++ b/arch/mips/au1000/common/usbdev.c | |||
@@ -0,0 +1,1557 @@ | |||
1 | /* | ||
2 | * BRIEF MODULE DESCRIPTION | ||
3 | * Au1000 USB Device-Side (device layer) | ||
4 | * | ||
5 | * Copyright 2001-2002 MontaVista Software Inc. | ||
6 | * Author: MontaVista Software, Inc. | ||
7 | * stevel@mvista.com or source@mvista.com | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License as published by the | ||
11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
12 | * option) any later version. | ||
13 | * | ||
14 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
15 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
16 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
17 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
20 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
21 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
24 | * | ||
25 | * You should have received a copy of the GNU General Public License along | ||
26 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
27 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
28 | */ | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/ioport.h> | ||
31 | #include <linux/sched.h> | ||
32 | #include <linux/signal.h> | ||
33 | #include <linux/errno.h> | ||
34 | #include <linux/poll.h> | ||
35 | #include <linux/init.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/fcntl.h> | ||
38 | #include <linux/module.h> | ||
39 | #include <linux/spinlock.h> | ||
40 | #include <linux/list.h> | ||
41 | #include <linux/smp_lock.h> | ||
42 | #define DEBUG | ||
43 | #include <linux/usb.h> | ||
44 | |||
45 | #include <asm/io.h> | ||
46 | #include <asm/uaccess.h> | ||
47 | #include <asm/irq.h> | ||
48 | #include <asm/mipsregs.h> | ||
49 | #include <asm/au1000.h> | ||
50 | #include <asm/au1000_dma.h> | ||
51 | #include <asm/au1000_usbdev.h> | ||
52 | |||
53 | #ifdef DEBUG | ||
54 | #undef VDEBUG | ||
55 | #ifdef VDEBUG | ||
56 | #define vdbg(fmt, arg...) printk(KERN_DEBUG __FILE__ ": " fmt "\n" , ## arg) | ||
57 | #else | ||
58 | #define vdbg(fmt, arg...) do {} while (0) | ||
59 | #endif | ||
60 | #else | ||
61 | #define vdbg(fmt, arg...) do {} while (0) | ||
62 | #endif | ||
63 | |||
64 | #define ALLOC_FLAGS (in_interrupt () ? GFP_ATOMIC : GFP_KERNEL) | ||
65 | |||
66 | #define EP_FIFO_DEPTH 8 | ||
67 | |||
68 | typedef enum { | ||
69 | SETUP_STAGE = 0, | ||
70 | DATA_STAGE, | ||
71 | STATUS_STAGE | ||
72 | } ep0_stage_t; | ||
73 | |||
74 | typedef struct { | ||
75 | int read_fifo; | ||
76 | int write_fifo; | ||
77 | int ctrl_stat; | ||
78 | int read_fifo_status; | ||
79 | int write_fifo_status; | ||
80 | } endpoint_reg_t; | ||
81 | |||
82 | typedef struct { | ||
83 | usbdev_pkt_t *head; | ||
84 | usbdev_pkt_t *tail; | ||
85 | int count; | ||
86 | } pkt_list_t; | ||
87 | |||
88 | typedef struct { | ||
89 | int active; | ||
90 | struct usb_endpoint_descriptor *desc; | ||
91 | endpoint_reg_t *reg; | ||
92 | /* Only one of these are used, unless this is the control ep */ | ||
93 | pkt_list_t inlist; | ||
94 | pkt_list_t outlist; | ||
95 | unsigned int indma, outdma; /* DMA channel numbers for IN, OUT */ | ||
96 | /* following are extracted from endpoint descriptor for easy access */ | ||
97 | int max_pkt_size; | ||
98 | int type; | ||
99 | int direction; | ||
100 | /* WE assign endpoint addresses! */ | ||
101 | int address; | ||
102 | spinlock_t lock; | ||
103 | } endpoint_t; | ||
104 | |||
105 | |||
106 | static struct usb_dev { | ||
107 | endpoint_t ep[6]; | ||
108 | ep0_stage_t ep0_stage; | ||
109 | |||
110 | struct usb_device_descriptor * dev_desc; | ||
111 | struct usb_interface_descriptor* if_desc; | ||
112 | struct usb_config_descriptor * conf_desc; | ||
113 | u8 * full_conf_desc; | ||
114 | struct usb_string_descriptor * str_desc[6]; | ||
115 | |||
116 | /* callback to function layer */ | ||
117 | void (*func_cb)(usbdev_cb_type_t type, unsigned long arg, | ||
118 | void *cb_data); | ||
119 | void* cb_data; | ||
120 | |||
121 | usbdev_state_t state; // device state | ||
122 | int suspended; // suspended flag | ||
123 | int address; // device address | ||
124 | int interface; | ||
125 | int num_ep; | ||
126 | u8 alternate_setting; | ||
127 | u8 configuration; // configuration value | ||
128 | int remote_wakeup_en; | ||
129 | } usbdev; | ||
130 | |||
131 | |||
132 | static endpoint_reg_t ep_reg[] = { | ||
133 | // FIFO's 0 and 1 are EP0 default control | ||
134 | {USBD_EP0RD, USBD_EP0WR, USBD_EP0CS, USBD_EP0RDSTAT, USBD_EP0WRSTAT }, | ||
135 | {0}, | ||
136 | // FIFO 2 is EP2, IN | ||
137 | { -1, USBD_EP2WR, USBD_EP2CS, -1, USBD_EP2WRSTAT }, | ||
138 | // FIFO 3 is EP3, IN | ||
139 | { -1, USBD_EP3WR, USBD_EP3CS, -1, USBD_EP3WRSTAT }, | ||
140 | // FIFO 4 is EP4, OUT | ||
141 | {USBD_EP4RD, -1, USBD_EP4CS, USBD_EP4RDSTAT, -1 }, | ||
142 | // FIFO 5 is EP5, OUT | ||
143 | {USBD_EP5RD, -1, USBD_EP5CS, USBD_EP5RDSTAT, -1 } | ||
144 | }; | ||
145 | |||
146 | static struct { | ||
147 | unsigned int id; | ||
148 | const char *str; | ||
149 | } ep_dma_id[] = { | ||
150 | { DMA_ID_USBDEV_EP0_TX, "USBDev EP0 IN" }, | ||
151 | { DMA_ID_USBDEV_EP0_RX, "USBDev EP0 OUT" }, | ||
152 | { DMA_ID_USBDEV_EP2_TX, "USBDev EP2 IN" }, | ||
153 | { DMA_ID_USBDEV_EP3_TX, "USBDev EP3 IN" }, | ||
154 | { DMA_ID_USBDEV_EP4_RX, "USBDev EP4 OUT" }, | ||
155 | { DMA_ID_USBDEV_EP5_RX, "USBDev EP5 OUT" } | ||
156 | }; | ||
157 | |||
158 | #define DIR_OUT 0 | ||
159 | #define DIR_IN (1<<3) | ||
160 | |||
161 | #define CONTROL_EP USB_ENDPOINT_XFER_CONTROL | ||
162 | #define BULK_EP USB_ENDPOINT_XFER_BULK | ||
163 | |||
164 | static inline endpoint_t * | ||
165 | epaddr_to_ep(struct usb_dev* dev, int ep_addr) | ||
166 | { | ||
167 | if (ep_addr >= 0 && ep_addr < 2) | ||
168 | return &dev->ep[0]; | ||
169 | if (ep_addr < 6) | ||
170 | return &dev->ep[ep_addr]; | ||
171 | return NULL; | ||
172 | } | ||
173 | |||
174 | static const char* std_req_name[] = { | ||
175 | "GET_STATUS", | ||
176 | "CLEAR_FEATURE", | ||
177 | "RESERVED", | ||
178 | "SET_FEATURE", | ||
179 | "RESERVED", | ||
180 | "SET_ADDRESS", | ||
181 | "GET_DESCRIPTOR", | ||
182 | "SET_DESCRIPTOR", | ||
183 | "GET_CONFIGURATION", | ||
184 | "SET_CONFIGURATION", | ||
185 | "GET_INTERFACE", | ||
186 | "SET_INTERFACE", | ||
187 | "SYNCH_FRAME" | ||
188 | }; | ||
189 | |||
190 | static inline const char* | ||
191 | get_std_req_name(int req) | ||
192 | { | ||
193 | return (req >= 0 && req <= 12) ? std_req_name[req] : "UNKNOWN"; | ||
194 | } | ||
195 | |||
196 | #if 0 | ||
197 | static void | ||
198 | dump_setup(struct usb_ctrlrequest* s) | ||
199 | { | ||
200 | dbg("%s: requesttype=%d", __FUNCTION__, s->requesttype); | ||
201 | dbg("%s: request=%d %s", __FUNCTION__, s->request, | ||
202 | get_std_req_name(s->request)); | ||
203 | dbg("%s: value=0x%04x", __FUNCTION__, s->wValue); | ||
204 | dbg("%s: index=%d", __FUNCTION__, s->index); | ||
205 | dbg("%s: length=%d", __FUNCTION__, s->length); | ||
206 | } | ||
207 | #endif | ||
208 | |||
209 | static inline usbdev_pkt_t * | ||
210 | alloc_packet(endpoint_t * ep, int data_size, void* data) | ||
211 | { | ||
212 | usbdev_pkt_t* pkt = kmalloc(sizeof(usbdev_pkt_t) + data_size, | ||
213 | ALLOC_FLAGS); | ||
214 | if (!pkt) | ||
215 | return NULL; | ||
216 | pkt->ep_addr = ep->address; | ||
217 | pkt->size = data_size; | ||
218 | pkt->status = 0; | ||
219 | pkt->next = NULL; | ||
220 | if (data) | ||
221 | memcpy(pkt->payload, data, data_size); | ||
222 | |||
223 | return pkt; | ||
224 | } | ||
225 | |||
226 | |||
227 | /* | ||
228 | * Link a packet to the tail of the enpoint's packet list. | ||
229 | * EP spinlock must be held when calling. | ||
230 | */ | ||
231 | static void | ||
232 | link_tail(endpoint_t * ep, pkt_list_t * list, usbdev_pkt_t * pkt) | ||
233 | { | ||
234 | if (!list->tail) { | ||
235 | list->head = list->tail = pkt; | ||
236 | list->count = 1; | ||
237 | } else { | ||
238 | list->tail->next = pkt; | ||
239 | list->tail = pkt; | ||
240 | list->count++; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | /* | ||
245 | * Unlink and return a packet from the head of the given packet | ||
246 | * list. It is the responsibility of the caller to free the packet. | ||
247 | * EP spinlock must be held when calling. | ||
248 | */ | ||
249 | static usbdev_pkt_t * | ||
250 | unlink_head(pkt_list_t * list) | ||
251 | { | ||
252 | usbdev_pkt_t *pkt; | ||
253 | |||
254 | pkt = list->head; | ||
255 | if (!pkt || !list->count) { | ||
256 | return NULL; | ||
257 | } | ||
258 | |||
259 | list->head = pkt->next; | ||
260 | if (!list->head) { | ||
261 | list->head = list->tail = NULL; | ||
262 | list->count = 0; | ||
263 | } else | ||
264 | list->count--; | ||
265 | |||
266 | return pkt; | ||
267 | } | ||
268 | |||
269 | /* | ||
270 | * Create and attach a new packet to the tail of the enpoint's | ||
271 | * packet list. EP spinlock must be held when calling. | ||
272 | */ | ||
273 | static usbdev_pkt_t * | ||
274 | add_packet(endpoint_t * ep, pkt_list_t * list, int size) | ||
275 | { | ||
276 | usbdev_pkt_t *pkt = alloc_packet(ep, size, NULL); | ||
277 | if (!pkt) | ||
278 | return NULL; | ||
279 | |||
280 | link_tail(ep, list, pkt); | ||
281 | return pkt; | ||
282 | } | ||
283 | |||
284 | |||
285 | /* | ||
286 | * Unlink and free a packet from the head of the enpoint's | ||
287 | * packet list. EP spinlock must be held when calling. | ||
288 | */ | ||
289 | static inline void | ||
290 | free_packet(pkt_list_t * list) | ||
291 | { | ||
292 | kfree(unlink_head(list)); | ||
293 | } | ||
294 | |||
295 | /* EP spinlock must be held when calling. */ | ||
296 | static inline void | ||
297 | flush_pkt_list(pkt_list_t * list) | ||
298 | { | ||
299 | while (list->count) | ||
300 | free_packet(list); | ||
301 | } | ||
302 | |||
303 | /* EP spinlock must be held when calling */ | ||
304 | static inline void | ||
305 | flush_write_fifo(endpoint_t * ep) | ||
306 | { | ||
307 | if (ep->reg->write_fifo_status >= 0) { | ||
308 | au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | | ||
309 | USBDEV_FSTAT_OF, | ||
310 | ep->reg->write_fifo_status); | ||
311 | //udelay(100); | ||
312 | //au_writel(USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, | ||
313 | // ep->reg->write_fifo_status); | ||
314 | } | ||
315 | } | ||
316 | |||
317 | /* EP spinlock must be held when calling */ | ||
318 | static inline void | ||
319 | flush_read_fifo(endpoint_t * ep) | ||
320 | { | ||
321 | if (ep->reg->read_fifo_status >= 0) { | ||
322 | au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | | ||
323 | USBDEV_FSTAT_OF, | ||
324 | ep->reg->read_fifo_status); | ||
325 | //udelay(100); | ||
326 | //au_writel(USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, | ||
327 | // ep->reg->read_fifo_status); | ||
328 | } | ||
329 | } | ||
330 | |||
331 | |||
332 | /* EP spinlock must be held when calling. */ | ||
333 | static void | ||
334 | endpoint_flush(endpoint_t * ep) | ||
335 | { | ||
336 | // First, flush all packets | ||
337 | flush_pkt_list(&ep->inlist); | ||
338 | flush_pkt_list(&ep->outlist); | ||
339 | |||
340 | // Now flush the endpoint's h/w FIFO(s) | ||
341 | flush_write_fifo(ep); | ||
342 | flush_read_fifo(ep); | ||
343 | } | ||
344 | |||
345 | /* EP spinlock must be held when calling. */ | ||
346 | static void | ||
347 | endpoint_stall(endpoint_t * ep) | ||
348 | { | ||
349 | u32 cs; | ||
350 | |||
351 | warn(__FUNCTION__); | ||
352 | |||
353 | cs = au_readl(ep->reg->ctrl_stat) | USBDEV_CS_STALL; | ||
354 | au_writel(cs, ep->reg->ctrl_stat); | ||
355 | } | ||
356 | |||
357 | /* EP spinlock must be held when calling. */ | ||
358 | static void | ||
359 | endpoint_unstall(endpoint_t * ep) | ||
360 | { | ||
361 | u32 cs; | ||
362 | |||
363 | warn(__FUNCTION__); | ||
364 | |||
365 | cs = au_readl(ep->reg->ctrl_stat) & ~USBDEV_CS_STALL; | ||
366 | au_writel(cs, ep->reg->ctrl_stat); | ||
367 | } | ||
368 | |||
369 | static void | ||
370 | endpoint_reset_datatoggle(endpoint_t * ep) | ||
371 | { | ||
372 | // FIXME: is this possible? | ||
373 | } | ||
374 | |||
375 | |||
376 | /* EP spinlock must be held when calling. */ | ||
377 | static int | ||
378 | endpoint_fifo_read(endpoint_t * ep) | ||
379 | { | ||
380 | int read_count = 0; | ||
381 | u8 *bufptr; | ||
382 | usbdev_pkt_t *pkt = ep->outlist.tail; | ||
383 | |||
384 | if (!pkt) | ||
385 | return -EINVAL; | ||
386 | |||
387 | bufptr = &pkt->payload[pkt->size]; | ||
388 | while (au_readl(ep->reg->read_fifo_status) & USBDEV_FSTAT_FCNT_MASK) { | ||
389 | *bufptr++ = au_readl(ep->reg->read_fifo) & 0xff; | ||
390 | read_count++; | ||
391 | pkt->size++; | ||
392 | } | ||
393 | |||
394 | return read_count; | ||
395 | } | ||
396 | |||
397 | #if 0 | ||
398 | /* EP spinlock must be held when calling. */ | ||
399 | static int | ||
400 | endpoint_fifo_write(endpoint_t * ep, int index) | ||
401 | { | ||
402 | int write_count = 0; | ||
403 | u8 *bufptr; | ||
404 | usbdev_pkt_t *pkt = ep->inlist.head; | ||
405 | |||
406 | if (!pkt) | ||
407 | return -EINVAL; | ||
408 | |||
409 | bufptr = &pkt->payload[index]; | ||
410 | while ((au_readl(ep->reg->write_fifo_status) & | ||
411 | USBDEV_FSTAT_FCNT_MASK) < EP_FIFO_DEPTH) { | ||
412 | if (bufptr < pkt->payload + pkt->size) { | ||
413 | au_writel(*bufptr++, ep->reg->write_fifo); | ||
414 | write_count++; | ||
415 | } else { | ||
416 | break; | ||
417 | } | ||
418 | } | ||
419 | |||
420 | return write_count; | ||
421 | } | ||
422 | #endif | ||
423 | |||
424 | /* | ||
425 | * This routine is called to restart transmission of a packet. | ||
426 | * The endpoint's TSIZE must be set to the new packet's size, | ||
427 | * and DMA to the write FIFO needs to be restarted. | ||
428 | * EP spinlock must be held when calling. | ||
429 | */ | ||
430 | static void | ||
431 | kickstart_send_packet(endpoint_t * ep) | ||
432 | { | ||
433 | u32 cs; | ||
434 | usbdev_pkt_t *pkt = ep->inlist.head; | ||
435 | |||
436 | vdbg("%s: ep%d, pkt=%p", __FUNCTION__, ep->address, pkt); | ||
437 | |||
438 | if (!pkt) { | ||
439 | err("%s: head=NULL! list->count=%d", __FUNCTION__, | ||
440 | ep->inlist.count); | ||
441 | return; | ||
442 | } | ||
443 | |||
444 | dma_cache_wback_inv((unsigned long)pkt->payload, pkt->size); | ||
445 | |||
446 | /* | ||
447 | * make sure FIFO is empty | ||
448 | */ | ||
449 | flush_write_fifo(ep); | ||
450 | |||
451 | cs = au_readl(ep->reg->ctrl_stat) & USBDEV_CS_STALL; | ||
452 | cs |= (pkt->size << USBDEV_CS_TSIZE_BIT); | ||
453 | au_writel(cs, ep->reg->ctrl_stat); | ||
454 | |||
455 | if (get_dma_active_buffer(ep->indma) == 1) { | ||
456 | set_dma_count1(ep->indma, pkt->size); | ||
457 | set_dma_addr1(ep->indma, virt_to_phys(pkt->payload)); | ||
458 | enable_dma_buffer1(ep->indma); // reenable | ||
459 | } else { | ||
460 | set_dma_count0(ep->indma, pkt->size); | ||
461 | set_dma_addr0(ep->indma, virt_to_phys(pkt->payload)); | ||
462 | enable_dma_buffer0(ep->indma); // reenable | ||
463 | } | ||
464 | if (dma_halted(ep->indma)) | ||
465 | start_dma(ep->indma); | ||
466 | } | ||
467 | |||
468 | |||
469 | /* | ||
470 | * This routine is called when a packet in the inlist has been | ||
471 | * completed. Frees the completed packet and starts sending the | ||
472 | * next. EP spinlock must be held when calling. | ||
473 | */ | ||
474 | static usbdev_pkt_t * | ||
475 | send_packet_complete(endpoint_t * ep) | ||
476 | { | ||
477 | usbdev_pkt_t *pkt = unlink_head(&ep->inlist); | ||
478 | |||
479 | if (pkt) { | ||
480 | pkt->status = | ||
481 | (au_readl(ep->reg->ctrl_stat) & USBDEV_CS_NAK) ? | ||
482 | PKT_STATUS_NAK : PKT_STATUS_ACK; | ||
483 | |||
484 | vdbg("%s: ep%d, %s pkt=%p, list count=%d", __FUNCTION__, | ||
485 | ep->address, (pkt->status & PKT_STATUS_NAK) ? | ||
486 | "NAK" : "ACK", pkt, ep->inlist.count); | ||
487 | } | ||
488 | |||
489 | /* | ||
490 | * The write fifo should already be drained if things are | ||
491 | * working right, but flush it anyway just in case. | ||
492 | */ | ||
493 | flush_write_fifo(ep); | ||
494 | |||
495 | // begin transmitting next packet in the inlist | ||
496 | if (ep->inlist.count) { | ||
497 | kickstart_send_packet(ep); | ||
498 | } | ||
499 | |||
500 | return pkt; | ||
501 | } | ||
502 | |||
503 | /* | ||
504 | * Add a new packet to the tail of the given ep's packet | ||
505 | * inlist. The transmit complete interrupt frees packets from | ||
506 | * the head of this list. EP spinlock must be held when calling. | ||
507 | */ | ||
508 | static int | ||
509 | send_packet(struct usb_dev* dev, usbdev_pkt_t *pkt, int async) | ||
510 | { | ||
511 | pkt_list_t *list; | ||
512 | endpoint_t* ep; | ||
513 | |||
514 | if (!pkt || !(ep = epaddr_to_ep(dev, pkt->ep_addr))) | ||
515 | return -EINVAL; | ||
516 | |||
517 | if (!pkt->size) | ||
518 | return 0; | ||
519 | |||
520 | list = &ep->inlist; | ||
521 | |||
522 | if (!async && list->count) { | ||
523 | halt_dma(ep->indma); | ||
524 | flush_pkt_list(list); | ||
525 | } | ||
526 | |||
527 | link_tail(ep, list, pkt); | ||
528 | |||
529 | vdbg("%s: ep%d, pkt=%p, size=%d, list count=%d", __FUNCTION__, | ||
530 | ep->address, pkt, pkt->size, list->count); | ||
531 | |||
532 | if (list->count == 1) { | ||
533 | /* | ||
534 | * if the packet count is one, it means the list was empty, | ||
535 | * and no more data will go out this ep until we kick-start | ||
536 | * it again. | ||
537 | */ | ||
538 | kickstart_send_packet(ep); | ||
539 | } | ||
540 | |||
541 | return pkt->size; | ||
542 | } | ||
543 | |||
544 | /* | ||
545 | * This routine is called to restart reception of a packet. | ||
546 | * EP spinlock must be held when calling. | ||
547 | */ | ||
548 | static void | ||
549 | kickstart_receive_packet(endpoint_t * ep) | ||
550 | { | ||
551 | usbdev_pkt_t *pkt; | ||
552 | |||
553 | // get and link a new packet for next reception | ||
554 | if (!(pkt = add_packet(ep, &ep->outlist, ep->max_pkt_size))) { | ||
555 | err("%s: could not alloc new packet", __FUNCTION__); | ||
556 | return; | ||
557 | } | ||
558 | |||
559 | if (get_dma_active_buffer(ep->outdma) == 1) { | ||
560 | clear_dma_done1(ep->outdma); | ||
561 | set_dma_count1(ep->outdma, ep->max_pkt_size); | ||
562 | set_dma_count0(ep->outdma, 0); | ||
563 | set_dma_addr1(ep->outdma, virt_to_phys(pkt->payload)); | ||
564 | enable_dma_buffer1(ep->outdma); // reenable | ||
565 | } else { | ||
566 | clear_dma_done0(ep->outdma); | ||
567 | set_dma_count0(ep->outdma, ep->max_pkt_size); | ||
568 | set_dma_count1(ep->outdma, 0); | ||
569 | set_dma_addr0(ep->outdma, virt_to_phys(pkt->payload)); | ||
570 | enable_dma_buffer0(ep->outdma); // reenable | ||
571 | } | ||
572 | if (dma_halted(ep->outdma)) | ||
573 | start_dma(ep->outdma); | ||
574 | } | ||
575 | |||
576 | |||
577 | /* | ||
578 | * This routine is called when a packet in the outlist has been | ||
579 | * completed (received) and we need to prepare for a new packet | ||
580 | * to be received. Halts DMA and computes the packet size from the | ||
581 | * remaining DMA counter. Then prepares a new packet for reception | ||
582 | * and restarts DMA. FIXME: what if another packet comes in | ||
583 | * on top of the completed packet? Counter would be wrong. | ||
584 | * EP spinlock must be held when calling. | ||
585 | */ | ||
586 | static usbdev_pkt_t * | ||
587 | receive_packet_complete(endpoint_t * ep) | ||
588 | { | ||
589 | usbdev_pkt_t *pkt = ep->outlist.tail; | ||
590 | u32 cs; | ||
591 | |||
592 | halt_dma(ep->outdma); | ||
593 | |||
594 | cs = au_readl(ep->reg->ctrl_stat); | ||
595 | |||
596 | if (!pkt) | ||
597 | return NULL; | ||
598 | |||
599 | pkt->size = ep->max_pkt_size - get_dma_residue(ep->outdma); | ||
600 | if (pkt->size) | ||
601 | dma_cache_inv((unsigned long)pkt->payload, pkt->size); | ||
602 | /* | ||
603 | * need to pull out any remaining bytes in the FIFO. | ||
604 | */ | ||
605 | endpoint_fifo_read(ep); | ||
606 | /* | ||
607 | * should be drained now, but flush anyway just in case. | ||
608 | */ | ||
609 | flush_read_fifo(ep); | ||
610 | |||
611 | pkt->status = (cs & USBDEV_CS_NAK) ? PKT_STATUS_NAK : PKT_STATUS_ACK; | ||
612 | if (ep->address == 0 && (cs & USBDEV_CS_SU)) | ||
613 | pkt->status |= PKT_STATUS_SU; | ||
614 | |||
615 | vdbg("%s: ep%d, %s pkt=%p, size=%d", __FUNCTION__, | ||
616 | ep->address, (pkt->status & PKT_STATUS_NAK) ? | ||
617 | "NAK" : "ACK", pkt, pkt->size); | ||
618 | |||
619 | kickstart_receive_packet(ep); | ||
620 | |||
621 | return pkt; | ||
622 | } | ||
623 | |||
624 | |||
625 | /* | ||
626 | **************************************************************************** | ||
627 | * Here starts the standard device request handlers. They are | ||
628 | * all called by do_setup() via a table of function pointers. | ||
629 | **************************************************************************** | ||
630 | */ | ||
631 | |||
632 | static ep0_stage_t | ||
633 | do_get_status(struct usb_dev* dev, struct usb_ctrlrequest* setup) | ||
634 | { | ||
635 | switch (setup->bRequestType) { | ||
636 | case 0x80: // Device | ||
637 | // FIXME: send device status | ||
638 | break; | ||
639 | case 0x81: // Interface | ||
640 | // FIXME: send interface status | ||
641 | break; | ||
642 | case 0x82: // End Point | ||
643 | // FIXME: send endpoint status | ||
644 | break; | ||
645 | default: | ||
646 | // Invalid Command | ||
647 | endpoint_stall(&dev->ep[0]); // Stall End Point 0 | ||
648 | break; | ||
649 | } | ||
650 | |||
651 | return STATUS_STAGE; | ||
652 | } | ||
653 | |||
654 | static ep0_stage_t | ||
655 | do_clear_feature(struct usb_dev* dev, struct usb_ctrlrequest* setup) | ||
656 | { | ||
657 | switch (setup->bRequestType) { | ||
658 | case 0x00: // Device | ||
659 | if ((le16_to_cpu(setup->wValue) & 0xff) == 1) | ||
660 | dev->remote_wakeup_en = 0; | ||
661 | else | ||
662 | endpoint_stall(&dev->ep[0]); | ||
663 | break; | ||
664 | case 0x02: // End Point | ||
665 | if ((le16_to_cpu(setup->wValue) & 0xff) == 0) { | ||
666 | endpoint_t *ep = | ||
667 | epaddr_to_ep(dev, | ||
668 | le16_to_cpu(setup->wIndex) & 0xff); | ||
669 | |||
670 | endpoint_unstall(ep); | ||
671 | endpoint_reset_datatoggle(ep); | ||
672 | } else | ||
673 | endpoint_stall(&dev->ep[0]); | ||
674 | break; | ||
675 | } | ||
676 | |||
677 | return SETUP_STAGE; | ||
678 | } | ||
679 | |||
680 | static ep0_stage_t | ||
681 | do_reserved(struct usb_dev* dev, struct usb_ctrlrequest* setup) | ||
682 | { | ||
683 | // Invalid request, stall End Point 0 | ||
684 | endpoint_stall(&dev->ep[0]); | ||
685 | return SETUP_STAGE; | ||
686 | } | ||
687 | |||
688 | static ep0_stage_t | ||
689 | do_set_feature(struct usb_dev* dev, struct usb_ctrlrequest* setup) | ||
690 | { | ||
691 | switch (setup->bRequestType) { | ||
692 | case 0x00: // Device | ||
693 | if ((le16_to_cpu(setup->wValue) & 0xff) == 1) | ||
694 | dev->remote_wakeup_en = 1; | ||
695 | else | ||
696 | endpoint_stall(&dev->ep[0]); | ||
697 | break; | ||
698 | case 0x02: // End Point | ||
699 | if ((le16_to_cpu(setup->wValue) & 0xff) == 0) { | ||
700 | endpoint_t *ep = | ||
701 | epaddr_to_ep(dev, | ||
702 | le16_to_cpu(setup->wIndex) & 0xff); | ||
703 | |||
704 | endpoint_stall(ep); | ||
705 | } else | ||
706 | endpoint_stall(&dev->ep[0]); | ||
707 | break; | ||
708 | } | ||
709 | |||
710 | return SETUP_STAGE; | ||
711 | } | ||
712 | |||
713 | static ep0_stage_t | ||
714 | do_set_address(struct usb_dev* dev, struct usb_ctrlrequest* setup) | ||
715 | { | ||
716 | int new_state = dev->state; | ||
717 | int new_addr = le16_to_cpu(setup->wValue); | ||
718 | |||
719 | dbg("%s: our address=%d", __FUNCTION__, new_addr); | ||
720 | |||
721 | if (new_addr > 127) { | ||
722 | // usb spec doesn't tell us what to do, so just go to | ||
723 | // default state | ||
724 | new_state = DEFAULT; | ||
725 | dev->address = 0; | ||
726 | } else if (dev->address != new_addr) { | ||
727 | dev->address = new_addr; | ||
728 | new_state = ADDRESS; | ||
729 | } | ||
730 | |||
731 | if (dev->state != new_state) { | ||
732 | dev->state = new_state; | ||
733 | /* inform function layer of usbdev state change */ | ||
734 | dev->func_cb(CB_NEW_STATE, dev->state, dev->cb_data); | ||
735 | } | ||
736 | |||
737 | return SETUP_STAGE; | ||
738 | } | ||
739 | |||
740 | static ep0_stage_t | ||
741 | do_get_descriptor(struct usb_dev* dev, struct usb_ctrlrequest* setup) | ||
742 | { | ||
743 | int strnum, desc_len = le16_to_cpu(setup->wLength); | ||
744 | |||
745 | switch (le16_to_cpu(setup->wValue) >> 8) { | ||
746 | case USB_DT_DEVICE: | ||
747 | // send device descriptor! | ||
748 | desc_len = desc_len > dev->dev_desc->bLength ? | ||
749 | dev->dev_desc->bLength : desc_len; | ||
750 | dbg("sending device desc, size=%d", desc_len); | ||
751 | send_packet(dev, alloc_packet(&dev->ep[0], desc_len, | ||
752 | dev->dev_desc), 0); | ||
753 | break; | ||
754 | case USB_DT_CONFIG: | ||
755 | // If the config descr index in low-byte of | ||
756 | // setup->wValue is valid, send config descr, | ||
757 | // otherwise stall ep0. | ||
758 | if ((le16_to_cpu(setup->wValue) & 0xff) == 0) { | ||
759 | // send config descriptor! | ||
760 | if (desc_len <= USB_DT_CONFIG_SIZE) { | ||
761 | dbg("sending partial config desc, size=%d", | ||
762 | desc_len); | ||
763 | send_packet(dev, | ||
764 | alloc_packet(&dev->ep[0], | ||
765 | desc_len, | ||
766 | dev->conf_desc), | ||
767 | 0); | ||
768 | } else { | ||
769 | int len = le16_to_cpu(dev->conf_desc->wTotalLength); | ||
770 | dbg("sending whole config desc," | ||
771 | " size=%d, our size=%d", desc_len, len); | ||
772 | desc_len = desc_len > len ? len : desc_len; | ||
773 | send_packet(dev, | ||
774 | alloc_packet(&dev->ep[0], | ||
775 | desc_len, | ||
776 | dev->full_conf_desc), | ||
777 | 0); | ||
778 | } | ||
779 | } else | ||
780 | endpoint_stall(&dev->ep[0]); | ||
781 | break; | ||
782 | case USB_DT_STRING: | ||
783 | // If the string descr index in low-byte of setup->wValue | ||
784 | // is valid, send string descr, otherwise stall ep0. | ||
785 | strnum = le16_to_cpu(setup->wValue) & 0xff; | ||
786 | if (strnum >= 0 && strnum < 6) { | ||
787 | struct usb_string_descriptor *desc = | ||
788 | dev->str_desc[strnum]; | ||
789 | desc_len = desc_len > desc->bLength ? | ||
790 | desc->bLength : desc_len; | ||
791 | dbg("sending string desc %d", strnum); | ||
792 | send_packet(dev, | ||
793 | alloc_packet(&dev->ep[0], desc_len, | ||
794 | desc), 0); | ||
795 | } else | ||
796 | endpoint_stall(&dev->ep[0]); | ||
797 | break; | ||
798 | default: | ||
799 | // Invalid request | ||
800 | err("invalid get desc=%d, stalled", | ||
801 | le16_to_cpu(setup->wValue) >> 8); | ||
802 | endpoint_stall(&dev->ep[0]); // Stall endpoint 0 | ||
803 | break; | ||
804 | } | ||
805 | |||
806 | return STATUS_STAGE; | ||
807 | } | ||
808 | |||
809 | static ep0_stage_t | ||
810 | do_set_descriptor(struct usb_dev* dev, struct usb_ctrlrequest* setup) | ||
811 | { | ||
812 | // TODO: implement | ||
813 | // there will be an OUT data stage (the descriptor to set) | ||
814 | return DATA_STAGE; | ||
815 | } | ||
816 | |||
817 | static ep0_stage_t | ||
818 | do_get_configuration(struct usb_dev* dev, struct usb_ctrlrequest* setup) | ||
819 | { | ||
820 | // send dev->configuration | ||
821 | dbg("sending config"); | ||
822 | send_packet(dev, alloc_packet(&dev->ep[0], 1, &dev->configuration), | ||
823 | 0); | ||
824 | return STATUS_STAGE; | ||
825 | } | ||
826 | |||
827 | static ep0_stage_t | ||
828 | do_set_configuration(struct usb_dev* dev, struct usb_ctrlrequest* setup) | ||
829 | { | ||
830 | // set active config to low-byte of setup->wValue | ||
831 | dev->configuration = le16_to_cpu(setup->wValue) & 0xff; | ||
832 | dbg("set config, config=%d", dev->configuration); | ||
833 | if (!dev->configuration && dev->state > DEFAULT) { | ||
834 | dev->state = ADDRESS; | ||
835 | /* inform function layer of usbdev state change */ | ||
836 | dev->func_cb(CB_NEW_STATE, dev->state, dev->cb_data); | ||
837 | } else if (dev->configuration == 1) { | ||
838 | dev->state = CONFIGURED; | ||
839 | /* inform function layer of usbdev state change */ | ||
840 | dev->func_cb(CB_NEW_STATE, dev->state, dev->cb_data); | ||
841 | } else { | ||
842 | // FIXME: "respond with request error" - how? | ||
843 | } | ||
844 | |||
845 | return SETUP_STAGE; | ||
846 | } | ||
847 | |||
848 | static ep0_stage_t | ||
849 | do_get_interface(struct usb_dev* dev, struct usb_ctrlrequest* setup) | ||
850 | { | ||
851 | // interface must be zero. | ||
852 | if ((le16_to_cpu(setup->wIndex) & 0xff) || dev->state == ADDRESS) { | ||
853 | // FIXME: respond with "request error". how? | ||
854 | } else if (dev->state == CONFIGURED) { | ||
855 | // send dev->alternate_setting | ||
856 | dbg("sending alt setting"); | ||
857 | send_packet(dev, alloc_packet(&dev->ep[0], 1, | ||
858 | &dev->alternate_setting), 0); | ||
859 | } | ||
860 | |||
861 | return STATUS_STAGE; | ||
862 | |||
863 | } | ||
864 | |||
865 | static ep0_stage_t | ||
866 | do_set_interface(struct usb_dev* dev, struct usb_ctrlrequest* setup) | ||
867 | { | ||
868 | if (dev->state == ADDRESS) { | ||
869 | // FIXME: respond with "request error". how? | ||
870 | } else if (dev->state == CONFIGURED) { | ||
871 | dev->interface = le16_to_cpu(setup->wIndex) & 0xff; | ||
872 | dev->alternate_setting = | ||
873 | le16_to_cpu(setup->wValue) & 0xff; | ||
874 | // interface and alternate_setting must be zero | ||
875 | if (dev->interface || dev->alternate_setting) { | ||
876 | // FIXME: respond with "request error". how? | ||
877 | } | ||
878 | } | ||
879 | |||
880 | return SETUP_STAGE; | ||
881 | } | ||
882 | |||
883 | static ep0_stage_t | ||
884 | do_synch_frame(struct usb_dev* dev, struct usb_ctrlrequest* setup) | ||
885 | { | ||
886 | // TODO | ||
887 | return SETUP_STAGE; | ||
888 | } | ||
889 | |||
890 | typedef ep0_stage_t (*req_method_t)(struct usb_dev* dev, | ||
891 | struct usb_ctrlrequest* setup); | ||
892 | |||
893 | |||
894 | /* Table of the standard device request handlers */ | ||
895 | static const req_method_t req_method[] = { | ||
896 | do_get_status, | ||
897 | do_clear_feature, | ||
898 | do_reserved, | ||
899 | do_set_feature, | ||
900 | do_reserved, | ||
901 | do_set_address, | ||
902 | do_get_descriptor, | ||
903 | do_set_descriptor, | ||
904 | do_get_configuration, | ||
905 | do_set_configuration, | ||
906 | do_get_interface, | ||
907 | do_set_interface, | ||
908 | do_synch_frame | ||
909 | }; | ||
910 | |||
911 | |||
912 | // SETUP packet request dispatcher | ||
913 | static void | ||
914 | do_setup (struct usb_dev* dev, struct usb_ctrlrequest* setup) | ||
915 | { | ||
916 | req_method_t m; | ||
917 | |||
918 | dbg("%s: req %d %s", __FUNCTION__, setup->bRequestType, | ||
919 | get_std_req_name(setup->bRequestType)); | ||
920 | |||
921 | if ((setup->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD || | ||
922 | (setup->bRequestType & USB_RECIP_MASK) != USB_RECIP_DEVICE) { | ||
923 | err("%s: invalid requesttype 0x%02x", __FUNCTION__, | ||
924 | setup->bRequestType); | ||
925 | return; | ||
926 | } | ||
927 | |||
928 | if ((setup->bRequestType & 0x80) == USB_DIR_OUT && setup->wLength) | ||
929 | dbg("%s: OUT phase! length=%d", __FUNCTION__, setup->wLength); | ||
930 | |||
931 | if (setup->bRequestType < sizeof(req_method)/sizeof(req_method_t)) | ||
932 | m = req_method[setup->bRequestType]; | ||
933 | else | ||
934 | m = do_reserved; | ||
935 | |||
936 | dev->ep0_stage = (*m)(dev, setup); | ||
937 | } | ||
938 | |||
939 | /* | ||
940 | * A SETUP, DATA0, or DATA1 packet has been received | ||
941 | * on the default control endpoint's fifo. | ||
942 | */ | ||
943 | static void | ||
944 | process_ep0_receive (struct usb_dev* dev) | ||
945 | { | ||
946 | endpoint_t *ep0 = &dev->ep[0]; | ||
947 | usbdev_pkt_t *pkt; | ||
948 | |||
949 | spin_lock(&ep0->lock); | ||
950 | |||
951 | // complete packet and prepare a new packet | ||
952 | pkt = receive_packet_complete(ep0); | ||
953 | if (!pkt) { | ||
954 | // FIXME: should put a warn/err here. | ||
955 | spin_unlock(&ep0->lock); | ||
956 | return; | ||
957 | } | ||
958 | |||
959 | // unlink immediately from endpoint. | ||
960 | unlink_head(&ep0->outlist); | ||
961 | |||
962 | // override current stage if h/w says it's a setup packet | ||
963 | if (pkt->status & PKT_STATUS_SU) | ||
964 | dev->ep0_stage = SETUP_STAGE; | ||
965 | |||
966 | switch (dev->ep0_stage) { | ||
967 | case SETUP_STAGE: | ||
968 | vdbg("SU bit is %s in setup stage", | ||
969 | (pkt->status & PKT_STATUS_SU) ? "set" : "not set"); | ||
970 | |||
971 | if (pkt->size == sizeof(struct usb_ctrlrequest)) { | ||
972 | #ifdef VDEBUG | ||
973 | if (pkt->status & PKT_STATUS_ACK) | ||
974 | vdbg("received SETUP"); | ||
975 | else | ||
976 | vdbg("received NAK SETUP"); | ||
977 | #endif | ||
978 | do_setup(dev, (struct usb_ctrlrequest*)pkt->payload); | ||
979 | } else | ||
980 | err("%s: wrong size SETUP received", __FUNCTION__); | ||
981 | break; | ||
982 | case DATA_STAGE: | ||
983 | /* | ||
984 | * this setup has an OUT data stage. Of the standard | ||
985 | * device requests, only set_descriptor has this stage, | ||
986 | * so this packet is that descriptor. TODO: drop it for | ||
987 | * now, set_descriptor not implemented. | ||
988 | * | ||
989 | * Need to place a byte in the write FIFO here, to prepare | ||
990 | * to send a zero-length DATA ack packet to the host in the | ||
991 | * STATUS stage. | ||
992 | */ | ||
993 | au_writel(0, ep0->reg->write_fifo); | ||
994 | dbg("received OUT stage DATAx on EP0, size=%d", pkt->size); | ||
995 | dev->ep0_stage = SETUP_STAGE; | ||
996 | break; | ||
997 | case STATUS_STAGE: | ||
998 | // this setup had an IN data stage, and host is ACK'ing | ||
999 | // the packet we sent during that stage. | ||
1000 | if (pkt->size != 0) | ||
1001 | warn("received non-zero ACK on EP0??"); | ||
1002 | #ifdef VDEBUG | ||
1003 | else | ||
1004 | vdbg("received ACK on EP0"); | ||
1005 | #endif | ||
1006 | dev->ep0_stage = SETUP_STAGE; | ||
1007 | break; | ||
1008 | } | ||
1009 | |||
1010 | spin_unlock(&ep0->lock); | ||
1011 | // we're done processing the packet, free it | ||
1012 | kfree(pkt); | ||
1013 | } | ||
1014 | |||
1015 | |||
1016 | /* | ||
1017 | * A DATA0/1 packet has been received on one of the OUT endpoints (4 or 5) | ||
1018 | */ | ||
1019 | static void | ||
1020 | process_ep_receive (struct usb_dev* dev, endpoint_t *ep) | ||
1021 | { | ||
1022 | usbdev_pkt_t *pkt; | ||
1023 | |||
1024 | spin_lock(&ep->lock); | ||
1025 | pkt = receive_packet_complete(ep); | ||
1026 | spin_unlock(&ep->lock); | ||
1027 | |||
1028 | dev->func_cb(CB_PKT_COMPLETE, (unsigned long)pkt, dev->cb_data); | ||
1029 | } | ||
1030 | |||
1031 | |||
1032 | |||
1033 | /* This ISR handles the receive complete and suspend events */ | ||
1034 | static void | ||
1035 | req_sus_intr (int irq, void *dev_id, struct pt_regs *regs) | ||
1036 | { | ||
1037 | struct usb_dev *dev = (struct usb_dev *) dev_id; | ||
1038 | u32 status; | ||
1039 | |||
1040 | status = au_readl(USBD_INTSTAT); | ||
1041 | au_writel(status, USBD_INTSTAT); // ack'em | ||
1042 | |||
1043 | if (status & (1<<0)) | ||
1044 | process_ep0_receive(dev); | ||
1045 | if (status & (1<<4)) | ||
1046 | process_ep_receive(dev, &dev->ep[4]); | ||
1047 | if (status & (1<<5)) | ||
1048 | process_ep_receive(dev, &dev->ep[5]); | ||
1049 | } | ||
1050 | |||
1051 | |||
1052 | /* This ISR handles the DMA done events on EP0 */ | ||
1053 | static void | ||
1054 | dma_done_ep0_intr(int irq, void *dev_id, struct pt_regs *regs) | ||
1055 | { | ||
1056 | struct usb_dev *dev = (struct usb_dev *) dev_id; | ||
1057 | usbdev_pkt_t* pkt; | ||
1058 | endpoint_t *ep0 = &dev->ep[0]; | ||
1059 | u32 cs0, buff_done; | ||
1060 | |||
1061 | spin_lock(&ep0->lock); | ||
1062 | cs0 = au_readl(ep0->reg->ctrl_stat); | ||
1063 | |||
1064 | // first check packet transmit done | ||
1065 | if ((buff_done = get_dma_buffer_done(ep0->indma)) != 0) { | ||
1066 | // transmitted a DATAx packet during DATA stage | ||
1067 | // on control endpoint 0 | ||
1068 | // clear DMA done bit | ||
1069 | if (buff_done & DMA_D0) | ||
1070 | clear_dma_done0(ep0->indma); | ||
1071 | if (buff_done & DMA_D1) | ||
1072 | clear_dma_done1(ep0->indma); | ||
1073 | |||
1074 | pkt = send_packet_complete(ep0); | ||
1075 | if (pkt) | ||
1076 | kfree(pkt); | ||
1077 | } | ||
1078 | |||
1079 | /* | ||
1080 | * Now check packet receive done. Shouldn't get these, | ||
1081 | * the receive packet complete intr should happen | ||
1082 | * before the DMA done intr occurs. | ||
1083 | */ | ||
1084 | if ((buff_done = get_dma_buffer_done(ep0->outdma)) != 0) { | ||
1085 | // clear DMA done bit | ||
1086 | if (buff_done & DMA_D0) | ||
1087 | clear_dma_done0(ep0->outdma); | ||
1088 | if (buff_done & DMA_D1) | ||
1089 | clear_dma_done1(ep0->outdma); | ||
1090 | |||
1091 | //process_ep0_receive(dev); | ||
1092 | } | ||
1093 | |||
1094 | spin_unlock(&ep0->lock); | ||
1095 | } | ||
1096 | |||
1097 | /* This ISR handles the DMA done events on endpoints 2,3,4,5 */ | ||
1098 | static void | ||
1099 | dma_done_ep_intr(int irq, void *dev_id, struct pt_regs *regs) | ||
1100 | { | ||
1101 | struct usb_dev *dev = (struct usb_dev *) dev_id; | ||
1102 | int i; | ||
1103 | |||
1104 | for (i = 2; i < 6; i++) { | ||
1105 | u32 buff_done; | ||
1106 | usbdev_pkt_t* pkt; | ||
1107 | endpoint_t *ep = &dev->ep[i]; | ||
1108 | |||
1109 | if (!ep->active) continue; | ||
1110 | |||
1111 | spin_lock(&ep->lock); | ||
1112 | |||
1113 | if (ep->direction == USB_DIR_IN) { | ||
1114 | buff_done = get_dma_buffer_done(ep->indma); | ||
1115 | if (buff_done != 0) { | ||
1116 | // transmitted a DATAx pkt on the IN ep | ||
1117 | // clear DMA done bit | ||
1118 | if (buff_done & DMA_D0) | ||
1119 | clear_dma_done0(ep->indma); | ||
1120 | if (buff_done & DMA_D1) | ||
1121 | clear_dma_done1(ep->indma); | ||
1122 | |||
1123 | pkt = send_packet_complete(ep); | ||
1124 | |||
1125 | spin_unlock(&ep->lock); | ||
1126 | dev->func_cb(CB_PKT_COMPLETE, | ||
1127 | (unsigned long)pkt, | ||
1128 | dev->cb_data); | ||
1129 | spin_lock(&ep->lock); | ||
1130 | } | ||
1131 | } else { | ||
1132 | /* | ||
1133 | * Check packet receive done (OUT ep). Shouldn't get | ||
1134 | * these, the rx packet complete intr should happen | ||
1135 | * before the DMA done intr occurs. | ||
1136 | */ | ||
1137 | buff_done = get_dma_buffer_done(ep->outdma); | ||
1138 | if (buff_done != 0) { | ||
1139 | // received a DATAx pkt on the OUT ep | ||
1140 | // clear DMA done bit | ||
1141 | if (buff_done & DMA_D0) | ||
1142 | clear_dma_done0(ep->outdma); | ||
1143 | if (buff_done & DMA_D1) | ||
1144 | clear_dma_done1(ep->outdma); | ||
1145 | |||
1146 | //process_ep_receive(dev, ep); | ||
1147 | } | ||
1148 | } | ||
1149 | |||
1150 | spin_unlock(&ep->lock); | ||
1151 | } | ||
1152 | } | ||
1153 | |||
1154 | |||
1155 | /*************************************************************************** | ||
1156 | * Here begins the external interface functions | ||
1157 | *************************************************************************** | ||
1158 | */ | ||
1159 | |||
1160 | /* | ||
1161 | * allocate a new packet | ||
1162 | */ | ||
1163 | int | ||
1164 | usbdev_alloc_packet(int ep_addr, int data_size, usbdev_pkt_t** pkt) | ||
1165 | { | ||
1166 | endpoint_t * ep = epaddr_to_ep(&usbdev, ep_addr); | ||
1167 | usbdev_pkt_t* lpkt = NULL; | ||
1168 | |||
1169 | if (!ep || !ep->active || ep->address < 2) | ||
1170 | return -ENODEV; | ||
1171 | if (data_size > ep->max_pkt_size) | ||
1172 | return -EINVAL; | ||
1173 | |||
1174 | lpkt = *pkt = alloc_packet(ep, data_size, NULL); | ||
1175 | if (!lpkt) | ||
1176 | return -ENOMEM; | ||
1177 | return 0; | ||
1178 | } | ||
1179 | |||
1180 | |||
1181 | /* | ||
1182 | * packet send | ||
1183 | */ | ||
1184 | int | ||
1185 | usbdev_send_packet(int ep_addr, usbdev_pkt_t * pkt) | ||
1186 | { | ||
1187 | unsigned long flags; | ||
1188 | int count; | ||
1189 | endpoint_t * ep; | ||
1190 | |||
1191 | if (!pkt || !(ep = epaddr_to_ep(&usbdev, pkt->ep_addr)) || | ||
1192 | !ep->active || ep->address < 2) | ||
1193 | return -ENODEV; | ||
1194 | if (ep->direction != USB_DIR_IN) | ||
1195 | return -EINVAL; | ||
1196 | |||
1197 | spin_lock_irqsave(&ep->lock, flags); | ||
1198 | count = send_packet(&usbdev, pkt, 1); | ||
1199 | spin_unlock_irqrestore(&ep->lock, flags); | ||
1200 | |||
1201 | return count; | ||
1202 | } | ||
1203 | |||
1204 | /* | ||
1205 | * packet receive | ||
1206 | */ | ||
1207 | int | ||
1208 | usbdev_receive_packet(int ep_addr, usbdev_pkt_t** pkt) | ||
1209 | { | ||
1210 | unsigned long flags; | ||
1211 | usbdev_pkt_t* lpkt = NULL; | ||
1212 | endpoint_t *ep = epaddr_to_ep(&usbdev, ep_addr); | ||
1213 | |||
1214 | if (!ep || !ep->active || ep->address < 2) | ||
1215 | return -ENODEV; | ||
1216 | if (ep->direction != USB_DIR_OUT) | ||
1217 | return -EINVAL; | ||
1218 | |||
1219 | spin_lock_irqsave(&ep->lock, flags); | ||
1220 | if (ep->outlist.count > 1) | ||
1221 | lpkt = unlink_head(&ep->outlist); | ||
1222 | spin_unlock_irqrestore(&ep->lock, flags); | ||
1223 | |||
1224 | if (!lpkt) { | ||
1225 | /* no packet available */ | ||
1226 | *pkt = NULL; | ||
1227 | return -ENODATA; | ||
1228 | } | ||
1229 | |||
1230 | *pkt = lpkt; | ||
1231 | |||
1232 | return lpkt->size; | ||
1233 | } | ||
1234 | |||
1235 | |||
1236 | /* | ||
1237 | * return total queued byte count on the endpoint. | ||
1238 | */ | ||
1239 | int | ||
1240 | usbdev_get_byte_count(int ep_addr) | ||
1241 | { | ||
1242 | unsigned long flags; | ||
1243 | pkt_list_t *list; | ||
1244 | usbdev_pkt_t *scan; | ||
1245 | int count = 0; | ||
1246 | endpoint_t * ep = epaddr_to_ep(&usbdev, ep_addr); | ||
1247 | |||
1248 | if (!ep || !ep->active || ep->address < 2) | ||
1249 | return -ENODEV; | ||
1250 | |||
1251 | if (ep->direction == USB_DIR_IN) { | ||
1252 | list = &ep->inlist; | ||
1253 | |||
1254 | spin_lock_irqsave(&ep->lock, flags); | ||
1255 | for (scan = list->head; scan; scan = scan->next) | ||
1256 | count += scan->size; | ||
1257 | spin_unlock_irqrestore(&ep->lock, flags); | ||
1258 | } else { | ||
1259 | list = &ep->outlist; | ||
1260 | |||
1261 | spin_lock_irqsave(&ep->lock, flags); | ||
1262 | if (list->count > 1) { | ||
1263 | for (scan = list->head; scan != list->tail; | ||
1264 | scan = scan->next) | ||
1265 | count += scan->size; | ||
1266 | } | ||
1267 | spin_unlock_irqrestore(&ep->lock, flags); | ||
1268 | } | ||
1269 | |||
1270 | return count; | ||
1271 | } | ||
1272 | |||
1273 | |||
1274 | void | ||
1275 | usbdev_exit(void) | ||
1276 | { | ||
1277 | endpoint_t *ep; | ||
1278 | int i; | ||
1279 | |||
1280 | au_writel(0, USBD_INTEN); // disable usb dev ints | ||
1281 | au_writel(0, USBD_ENABLE); // disable usb dev | ||
1282 | |||
1283 | free_irq(AU1000_USB_DEV_REQ_INT, &usbdev); | ||
1284 | free_irq(AU1000_USB_DEV_SUS_INT, &usbdev); | ||
1285 | |||
1286 | // free all control endpoint resources | ||
1287 | ep = &usbdev.ep[0]; | ||
1288 | free_au1000_dma(ep->indma); | ||
1289 | free_au1000_dma(ep->outdma); | ||
1290 | endpoint_flush(ep); | ||
1291 | |||
1292 | // free ep resources | ||
1293 | for (i = 2; i < 6; i++) { | ||
1294 | ep = &usbdev.ep[i]; | ||
1295 | if (!ep->active) continue; | ||
1296 | |||
1297 | if (ep->direction == USB_DIR_IN) { | ||
1298 | free_au1000_dma(ep->indma); | ||
1299 | } else { | ||
1300 | free_au1000_dma(ep->outdma); | ||
1301 | } | ||
1302 | endpoint_flush(ep); | ||
1303 | } | ||
1304 | |||
1305 | if (usbdev.full_conf_desc) | ||
1306 | kfree(usbdev.full_conf_desc); | ||
1307 | } | ||
1308 | |||
1309 | int | ||
1310 | usbdev_init(struct usb_device_descriptor* dev_desc, | ||
1311 | struct usb_config_descriptor* config_desc, | ||
1312 | struct usb_interface_descriptor* if_desc, | ||
1313 | struct usb_endpoint_descriptor* ep_desc, | ||
1314 | struct usb_string_descriptor* str_desc[], | ||
1315 | void (*cb)(usbdev_cb_type_t, unsigned long, void *), | ||
1316 | void* cb_data) | ||
1317 | { | ||
1318 | endpoint_t *ep0; | ||
1319 | int i, ret=0; | ||
1320 | u8* fcd; | ||
1321 | |||
1322 | if (dev_desc->bNumConfigurations > 1 || | ||
1323 | config_desc->bNumInterfaces > 1 || | ||
1324 | if_desc->bNumEndpoints > 4) { | ||
1325 | err("Only one config, one i/f, and no more " | ||
1326 | "than 4 ep's allowed"); | ||
1327 | ret = -EINVAL; | ||
1328 | goto out; | ||
1329 | } | ||
1330 | |||
1331 | if (!cb) { | ||
1332 | err("Function-layer callback required"); | ||
1333 | ret = -EINVAL; | ||
1334 | goto out; | ||
1335 | } | ||
1336 | |||
1337 | if (dev_desc->bMaxPacketSize0 != USBDEV_EP0_MAX_PACKET_SIZE) { | ||
1338 | warn("EP0 Max Packet size must be %d", | ||
1339 | USBDEV_EP0_MAX_PACKET_SIZE); | ||
1340 | dev_desc->bMaxPacketSize0 = USBDEV_EP0_MAX_PACKET_SIZE; | ||
1341 | } | ||
1342 | |||
1343 | memset(&usbdev, 0, sizeof(struct usb_dev)); | ||
1344 | |||
1345 | usbdev.state = DEFAULT; | ||
1346 | usbdev.dev_desc = dev_desc; | ||
1347 | usbdev.if_desc = if_desc; | ||
1348 | usbdev.conf_desc = config_desc; | ||
1349 | for (i=0; i<6; i++) | ||
1350 | usbdev.str_desc[i] = str_desc[i]; | ||
1351 | usbdev.func_cb = cb; | ||
1352 | usbdev.cb_data = cb_data; | ||
1353 | |||
1354 | /* Initialize default control endpoint */ | ||
1355 | ep0 = &usbdev.ep[0]; | ||
1356 | ep0->active = 1; | ||
1357 | ep0->type = CONTROL_EP; | ||
1358 | ep0->max_pkt_size = USBDEV_EP0_MAX_PACKET_SIZE; | ||
1359 | spin_lock_init(&ep0->lock); | ||
1360 | ep0->desc = NULL; // ep0 has no descriptor | ||
1361 | ep0->address = 0; | ||
1362 | ep0->direction = 0; | ||
1363 | ep0->reg = &ep_reg[0]; | ||
1364 | |||
1365 | /* Initialize the other requested endpoints */ | ||
1366 | for (i = 0; i < if_desc->bNumEndpoints; i++) { | ||
1367 | struct usb_endpoint_descriptor* epd = &ep_desc[i]; | ||
1368 | endpoint_t *ep; | ||
1369 | |||
1370 | if ((epd->bEndpointAddress & 0x80) == USB_DIR_IN) { | ||
1371 | ep = &usbdev.ep[2]; | ||
1372 | ep->address = 2; | ||
1373 | if (ep->active) { | ||
1374 | ep = &usbdev.ep[3]; | ||
1375 | ep->address = 3; | ||
1376 | if (ep->active) { | ||
1377 | err("too many IN ep's requested"); | ||
1378 | ret = -ENODEV; | ||
1379 | goto out; | ||
1380 | } | ||
1381 | } | ||
1382 | } else { | ||
1383 | ep = &usbdev.ep[4]; | ||
1384 | ep->address = 4; | ||
1385 | if (ep->active) { | ||
1386 | ep = &usbdev.ep[5]; | ||
1387 | ep->address = 5; | ||
1388 | if (ep->active) { | ||
1389 | err("too many OUT ep's requested"); | ||
1390 | ret = -ENODEV; | ||
1391 | goto out; | ||
1392 | } | ||
1393 | } | ||
1394 | } | ||
1395 | |||
1396 | ep->active = 1; | ||
1397 | epd->bEndpointAddress &= ~0x0f; | ||
1398 | epd->bEndpointAddress |= (u8)ep->address; | ||
1399 | ep->direction = epd->bEndpointAddress & 0x80; | ||
1400 | ep->type = epd->bmAttributes & 0x03; | ||
1401 | ep->max_pkt_size = le16_to_cpu(epd->wMaxPacketSize); | ||
1402 | spin_lock_init(&ep->lock); | ||
1403 | ep->desc = epd; | ||
1404 | ep->reg = &ep_reg[ep->address]; | ||
1405 | } | ||
1406 | |||
1407 | /* | ||
1408 | * initialize the full config descriptor | ||
1409 | */ | ||
1410 | usbdev.full_conf_desc = fcd = kmalloc(le16_to_cpu(config_desc->wTotalLength), | ||
1411 | ALLOC_FLAGS); | ||
1412 | if (!fcd) { | ||
1413 | err("failed to alloc full config descriptor"); | ||
1414 | ret = -ENOMEM; | ||
1415 | goto out; | ||
1416 | } | ||
1417 | |||
1418 | memcpy(fcd, config_desc, USB_DT_CONFIG_SIZE); | ||
1419 | fcd += USB_DT_CONFIG_SIZE; | ||
1420 | memcpy(fcd, if_desc, USB_DT_INTERFACE_SIZE); | ||
1421 | fcd += USB_DT_INTERFACE_SIZE; | ||
1422 | for (i = 0; i < if_desc->bNumEndpoints; i++) { | ||
1423 | memcpy(fcd, &ep_desc[i], USB_DT_ENDPOINT_SIZE); | ||
1424 | fcd += USB_DT_ENDPOINT_SIZE; | ||
1425 | } | ||
1426 | |||
1427 | /* Now we're ready to enable the controller */ | ||
1428 | au_writel(0x0002, USBD_ENABLE); | ||
1429 | udelay(100); | ||
1430 | au_writel(0x0003, USBD_ENABLE); | ||
1431 | udelay(100); | ||
1432 | |||
1433 | /* build and send config table based on ep descriptors */ | ||
1434 | for (i = 0; i < 6; i++) { | ||
1435 | endpoint_t *ep; | ||
1436 | if (i == 1) | ||
1437 | continue; // skip dummy ep | ||
1438 | ep = &usbdev.ep[i]; | ||
1439 | if (ep->active) { | ||
1440 | au_writel((ep->address << 4) | 0x04, USBD_CONFIG); | ||
1441 | au_writel(((ep->max_pkt_size & 0x380) >> 7) | | ||
1442 | (ep->direction >> 4) | (ep->type << 4), | ||
1443 | USBD_CONFIG); | ||
1444 | au_writel((ep->max_pkt_size & 0x7f) << 1, USBD_CONFIG); | ||
1445 | au_writel(0x00, USBD_CONFIG); | ||
1446 | au_writel(ep->address, USBD_CONFIG); | ||
1447 | } else { | ||
1448 | u8 dir = (i==2 || i==3) ? DIR_IN : DIR_OUT; | ||
1449 | au_writel((i << 4) | 0x04, USBD_CONFIG); | ||
1450 | au_writel(((16 & 0x380) >> 7) | dir | | ||
1451 | (BULK_EP << 4), USBD_CONFIG); | ||
1452 | au_writel((16 & 0x7f) << 1, USBD_CONFIG); | ||
1453 | au_writel(0x00, USBD_CONFIG); | ||
1454 | au_writel(i, USBD_CONFIG); | ||
1455 | } | ||
1456 | } | ||
1457 | |||
1458 | /* | ||
1459 | * Enable Receive FIFO Complete interrupts only. Transmit | ||
1460 | * complete is being handled by the DMA done interrupts. | ||
1461 | */ | ||
1462 | au_writel(0x31, USBD_INTEN); | ||
1463 | |||
1464 | /* | ||
1465 | * Controller is now enabled, request DMA and IRQ | ||
1466 | * resources. | ||
1467 | */ | ||
1468 | |||
1469 | /* request the USB device transfer complete interrupt */ | ||
1470 | if (request_irq(AU1000_USB_DEV_REQ_INT, req_sus_intr, SA_INTERRUPT, | ||
1471 | "USBdev req", &usbdev)) { | ||
1472 | err("Can't get device request intr"); | ||
1473 | ret = -ENXIO; | ||
1474 | goto out; | ||
1475 | } | ||
1476 | /* request the USB device suspend interrupt */ | ||
1477 | if (request_irq(AU1000_USB_DEV_SUS_INT, req_sus_intr, SA_INTERRUPT, | ||
1478 | "USBdev sus", &usbdev)) { | ||
1479 | err("Can't get device suspend intr"); | ||
1480 | ret = -ENXIO; | ||
1481 | goto out; | ||
1482 | } | ||
1483 | |||
1484 | /* Request EP0 DMA and IRQ */ | ||
1485 | if ((ep0->indma = request_au1000_dma(ep_dma_id[0].id, | ||
1486 | ep_dma_id[0].str, | ||
1487 | dma_done_ep0_intr, | ||
1488 | SA_INTERRUPT, | ||
1489 | &usbdev)) < 0) { | ||
1490 | err("Can't get %s DMA", ep_dma_id[0].str); | ||
1491 | ret = -ENXIO; | ||
1492 | goto out; | ||
1493 | } | ||
1494 | if ((ep0->outdma = request_au1000_dma(ep_dma_id[1].id, | ||
1495 | ep_dma_id[1].str, | ||
1496 | NULL, 0, NULL)) < 0) { | ||
1497 | err("Can't get %s DMA", ep_dma_id[1].str); | ||
1498 | ret = -ENXIO; | ||
1499 | goto out; | ||
1500 | } | ||
1501 | |||
1502 | // Flush the ep0 buffers and FIFOs | ||
1503 | endpoint_flush(ep0); | ||
1504 | // start packet reception on ep0 | ||
1505 | kickstart_receive_packet(ep0); | ||
1506 | |||
1507 | /* Request DMA and IRQ for the other endpoints */ | ||
1508 | for (i = 2; i < 6; i++) { | ||
1509 | endpoint_t *ep = &usbdev.ep[i]; | ||
1510 | if (!ep->active) | ||
1511 | continue; | ||
1512 | |||
1513 | // Flush the endpoint buffers and FIFOs | ||
1514 | endpoint_flush(ep); | ||
1515 | |||
1516 | if (ep->direction == USB_DIR_IN) { | ||
1517 | ep->indma = | ||
1518 | request_au1000_dma(ep_dma_id[ep->address].id, | ||
1519 | ep_dma_id[ep->address].str, | ||
1520 | dma_done_ep_intr, | ||
1521 | SA_INTERRUPT, | ||
1522 | &usbdev); | ||
1523 | if (ep->indma < 0) { | ||
1524 | err("Can't get %s DMA", | ||
1525 | ep_dma_id[ep->address].str); | ||
1526 | ret = -ENXIO; | ||
1527 | goto out; | ||
1528 | } | ||
1529 | } else { | ||
1530 | ep->outdma = | ||
1531 | request_au1000_dma(ep_dma_id[ep->address].id, | ||
1532 | ep_dma_id[ep->address].str, | ||
1533 | NULL, 0, NULL); | ||
1534 | if (ep->outdma < 0) { | ||
1535 | err("Can't get %s DMA", | ||
1536 | ep_dma_id[ep->address].str); | ||
1537 | ret = -ENXIO; | ||
1538 | goto out; | ||
1539 | } | ||
1540 | |||
1541 | // start packet reception on OUT endpoint | ||
1542 | kickstart_receive_packet(ep); | ||
1543 | } | ||
1544 | } | ||
1545 | |||
1546 | out: | ||
1547 | if (ret) | ||
1548 | usbdev_exit(); | ||
1549 | return ret; | ||
1550 | } | ||
1551 | |||
1552 | EXPORT_SYMBOL(usbdev_init); | ||
1553 | EXPORT_SYMBOL(usbdev_exit); | ||
1554 | EXPORT_SYMBOL(usbdev_alloc_packet); | ||
1555 | EXPORT_SYMBOL(usbdev_receive_packet); | ||
1556 | EXPORT_SYMBOL(usbdev_send_packet); | ||
1557 | EXPORT_SYMBOL(usbdev_get_byte_count); | ||