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/ppc/8xx_io |
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/ppc/8xx_io')
-rw-r--r-- | arch/ppc/8xx_io/Kconfig | 138 | ||||
-rw-r--r-- | arch/ppc/8xx_io/Makefile | 10 | ||||
-rw-r--r-- | arch/ppc/8xx_io/commproc.c | 464 | ||||
-rw-r--r-- | arch/ppc/8xx_io/cs4218.h | 167 | ||||
-rw-r--r-- | arch/ppc/8xx_io/cs4218_tdm.c | 2836 | ||||
-rw-r--r-- | arch/ppc/8xx_io/enet.c | 971 | ||||
-rw-r--r-- | arch/ppc/8xx_io/fec.c | 1973 | ||||
-rw-r--r-- | arch/ppc/8xx_io/micropatch.c | 744 |
8 files changed, 7303 insertions, 0 deletions
diff --git a/arch/ppc/8xx_io/Kconfig b/arch/ppc/8xx_io/Kconfig new file mode 100644 index 000000000000..9e2227ec3b34 --- /dev/null +++ b/arch/ppc/8xx_io/Kconfig | |||
@@ -0,0 +1,138 @@ | |||
1 | # | ||
2 | # MPC8xx Communication options | ||
3 | # | ||
4 | |||
5 | menu "MPC8xx CPM Options" | ||
6 | depends on 8xx | ||
7 | |||
8 | config SCC_ENET | ||
9 | bool "CPM SCC Ethernet" | ||
10 | depends on NET_ETHERNET | ||
11 | help | ||
12 | Enable Ethernet support via the Motorola MPC8xx serial | ||
13 | communications controller. | ||
14 | |||
15 | choice | ||
16 | prompt "SCC used for Ethernet" | ||
17 | depends on SCC_ENET | ||
18 | default SCC1_ENET | ||
19 | |||
20 | config SCC1_ENET | ||
21 | bool "SCC1" | ||
22 | help | ||
23 | Use MPC8xx serial communications controller 1 to drive Ethernet | ||
24 | (default). | ||
25 | |||
26 | config SCC2_ENET | ||
27 | bool "SCC2" | ||
28 | help | ||
29 | Use MPC8xx serial communications controller 2 to drive Ethernet. | ||
30 | |||
31 | config SCC3_ENET | ||
32 | bool "SCC3" | ||
33 | help | ||
34 | Use MPC8xx serial communications controller 3 to drive Ethernet. | ||
35 | |||
36 | endchoice | ||
37 | |||
38 | config FEC_ENET | ||
39 | bool "860T FEC Ethernet" | ||
40 | depends on NET_ETHERNET | ||
41 | help | ||
42 | Enable Ethernet support via the Fast Ethernet Controller (FCC) on | ||
43 | the Motorola MPC8260. | ||
44 | |||
45 | config USE_MDIO | ||
46 | bool "Use MDIO for PHY configuration" | ||
47 | depends on FEC_ENET | ||
48 | help | ||
49 | On some boards the hardware configuration of the ethernet PHY can be | ||
50 | used without any software interaction over the MDIO interface, so | ||
51 | all MII code can be omitted. Say N here if unsure or if you don't | ||
52 | need link status reports. | ||
53 | |||
54 | config FEC_AM79C874 | ||
55 | bool "Support AMD79C874 PHY" | ||
56 | depends on USE_MDIO | ||
57 | |||
58 | config FEC_LXT970 | ||
59 | bool "Support LXT970 PHY" | ||
60 | depends on USE_MDIO | ||
61 | |||
62 | config FEC_LXT971 | ||
63 | bool "Support LXT971 PHY" | ||
64 | depends on USE_MDIO | ||
65 | |||
66 | config FEC_QS6612 | ||
67 | bool "Support QS6612 PHY" | ||
68 | depends on USE_MDIO | ||
69 | |||
70 | config ENET_BIG_BUFFERS | ||
71 | bool "Use Big CPM Ethernet Buffers" | ||
72 | depends on NET_ETHERNET | ||
73 | help | ||
74 | Allocate large buffers for MPC8xx Etherenet. Increases throughput | ||
75 | and decreases the likelihood of dropped packets, but costs memory. | ||
76 | |||
77 | config HTDMSOUND | ||
78 | bool "Embedded Planet HIOX Audio" | ||
79 | depends on SOUND=y | ||
80 | |||
81 | # This doesn't really belong here, but it is convenient to ask | ||
82 | # 8xx specific questions. | ||
83 | comment "Generic MPC8xx Options" | ||
84 | |||
85 | config 8xx_COPYBACK | ||
86 | bool "Copy-Back Data Cache (else Writethrough)" | ||
87 | help | ||
88 | Saying Y here will cause the cache on an MPC8xx processor to be used | ||
89 | in Copy-Back mode. If you say N here, it is used in Writethrough | ||
90 | mode. | ||
91 | |||
92 | If in doubt, say Y here. | ||
93 | |||
94 | config 8xx_CPU6 | ||
95 | bool "CPU6 Silicon Errata (860 Pre Rev. C)" | ||
96 | help | ||
97 | MPC860 CPUs, prior to Rev C have some bugs in the silicon, which | ||
98 | require workarounds for Linux (and most other OSes to work). If you | ||
99 | get a BUG() very early in boot, this might fix the problem. For | ||
100 | more details read the document entitled "MPC860 Family Device Errata | ||
101 | Reference" on Motorola's website. This option also incurs a | ||
102 | performance hit. | ||
103 | |||
104 | If in doubt, say N here. | ||
105 | |||
106 | choice | ||
107 | prompt "Microcode patch selection" | ||
108 | default NO_UCODE_PATCH | ||
109 | help | ||
110 | Help not implemented yet, coming soon. | ||
111 | |||
112 | config NO_UCODE_PATCH | ||
113 | bool "None" | ||
114 | |||
115 | config USB_SOF_UCODE_PATCH | ||
116 | bool "USB SOF patch" | ||
117 | help | ||
118 | Help not implemented yet, coming soon. | ||
119 | |||
120 | config I2C_SPI_UCODE_PATCH | ||
121 | bool "I2C/SPI relocation patch" | ||
122 | help | ||
123 | Help not implemented yet, coming soon. | ||
124 | |||
125 | config I2C_SPI_SMC1_UCODE_PATCH | ||
126 | bool "I2C/SPI/SMC1 relocation patch" | ||
127 | help | ||
128 | Help not implemented yet, coming soon. | ||
129 | |||
130 | endchoice | ||
131 | |||
132 | config UCODE_PATCH | ||
133 | bool | ||
134 | default y | ||
135 | depends on !NO_UCODE_PATCH | ||
136 | |||
137 | endmenu | ||
138 | |||
diff --git a/arch/ppc/8xx_io/Makefile b/arch/ppc/8xx_io/Makefile new file mode 100644 index 000000000000..d8760181fe99 --- /dev/null +++ b/arch/ppc/8xx_io/Makefile | |||
@@ -0,0 +1,10 @@ | |||
1 | # | ||
2 | # Makefile for the linux MPC8xx ppc-specific parts of comm processor | ||
3 | # | ||
4 | |||
5 | obj-y := commproc.o | ||
6 | |||
7 | obj-$(CONFIG_FEC_ENET) += fec.o | ||
8 | obj-$(CONFIG_SCC_ENET) += enet.o | ||
9 | obj-$(CONFIG_UCODE_PATCH) += micropatch.o | ||
10 | obj-$(CONFIG_HTDMSOUND) += cs4218_tdm.o | ||
diff --git a/arch/ppc/8xx_io/commproc.c b/arch/ppc/8xx_io/commproc.c new file mode 100644 index 000000000000..0cc2e7a9cb11 --- /dev/null +++ b/arch/ppc/8xx_io/commproc.c | |||
@@ -0,0 +1,464 @@ | |||
1 | /* | ||
2 | * General Purpose functions for the global management of the | ||
3 | * Communication Processor Module. | ||
4 | * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) | ||
5 | * | ||
6 | * In addition to the individual control of the communication | ||
7 | * channels, there are a few functions that globally affect the | ||
8 | * communication processor. | ||
9 | * | ||
10 | * Buffer descriptors must be allocated from the dual ported memory | ||
11 | * space. The allocator for that is here. When the communication | ||
12 | * process is reset, we reclaim the memory available. There is | ||
13 | * currently no deallocator for this memory. | ||
14 | * The amount of space available is platform dependent. On the | ||
15 | * MBX, the EPPC software loads additional microcode into the | ||
16 | * communication processor, and uses some of the DP ram for this | ||
17 | * purpose. Current, the first 512 bytes and the last 256 bytes of | ||
18 | * memory are used. Right now I am conservative and only use the | ||
19 | * memory that can never be used for microcode. If there are | ||
20 | * applications that require more DP ram, we can expand the boundaries | ||
21 | * but then we have to be careful of any downloaded microcode. | ||
22 | */ | ||
23 | #include <linux/errno.h> | ||
24 | #include <linux/sched.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/dma-mapping.h> | ||
27 | #include <linux/param.h> | ||
28 | #include <linux/string.h> | ||
29 | #include <linux/mm.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <linux/irq.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <asm/mpc8xx.h> | ||
34 | #include <asm/page.h> | ||
35 | #include <asm/pgtable.h> | ||
36 | #include <asm/8xx_immap.h> | ||
37 | #include <asm/commproc.h> | ||
38 | #include <asm/io.h> | ||
39 | #include <asm/tlbflush.h> | ||
40 | #include <asm/rheap.h> | ||
41 | |||
42 | extern int get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep); | ||
43 | |||
44 | static void m8xx_cpm_dpinit(void); | ||
45 | static uint host_buffer; /* One page of host buffer */ | ||
46 | static uint host_end; /* end + 1 */ | ||
47 | cpm8xx_t *cpmp; /* Pointer to comm processor space */ | ||
48 | |||
49 | /* CPM interrupt vector functions. | ||
50 | */ | ||
51 | struct cpm_action { | ||
52 | void (*handler)(void *, struct pt_regs * regs); | ||
53 | void *dev_id; | ||
54 | }; | ||
55 | static struct cpm_action cpm_vecs[CPMVEC_NR]; | ||
56 | static irqreturn_t cpm_interrupt(int irq, void * dev, struct pt_regs * regs); | ||
57 | static irqreturn_t cpm_error_interrupt(int irq, void *dev, struct pt_regs * regs); | ||
58 | static void alloc_host_memory(void); | ||
59 | /* Define a table of names to identify CPM interrupt handlers in | ||
60 | * /proc/interrupts. | ||
61 | */ | ||
62 | const char *cpm_int_name[] = | ||
63 | { "error", "PC4", "PC5", "SMC2", | ||
64 | "SMC1", "SPI", "PC6", "Timer 4", | ||
65 | "", "PC7", "PC8", "PC9", | ||
66 | "Timer 3", "", "PC10", "PC11", | ||
67 | "I2C", "RISC Timer", "Timer 2", "", | ||
68 | "IDMA2", "IDMA1", "SDMA error", "PC12", | ||
69 | "PC13", "Timer 1", "PC14", "SCC4", | ||
70 | "SCC3", "SCC2", "SCC1", "PC15" | ||
71 | }; | ||
72 | |||
73 | static void | ||
74 | cpm_mask_irq(unsigned int irq) | ||
75 | { | ||
76 | int cpm_vec = irq - CPM_IRQ_OFFSET; | ||
77 | |||
78 | ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr &= ~(1 << cpm_vec); | ||
79 | } | ||
80 | |||
81 | static void | ||
82 | cpm_unmask_irq(unsigned int irq) | ||
83 | { | ||
84 | int cpm_vec = irq - CPM_IRQ_OFFSET; | ||
85 | |||
86 | ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr |= (1 << cpm_vec); | ||
87 | } | ||
88 | |||
89 | static void | ||
90 | cpm_ack(unsigned int irq) | ||
91 | { | ||
92 | /* We do not need to do anything here. */ | ||
93 | } | ||
94 | |||
95 | static void | ||
96 | cpm_eoi(unsigned int irq) | ||
97 | { | ||
98 | int cpm_vec = irq - CPM_IRQ_OFFSET; | ||
99 | |||
100 | ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cisr = (1 << cpm_vec); | ||
101 | } | ||
102 | |||
103 | struct hw_interrupt_type cpm_pic = { | ||
104 | .typename = " CPM ", | ||
105 | .enable = cpm_unmask_irq, | ||
106 | .disable = cpm_mask_irq, | ||
107 | .ack = cpm_ack, | ||
108 | .end = cpm_eoi, | ||
109 | }; | ||
110 | |||
111 | extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr); | ||
112 | |||
113 | void | ||
114 | m8xx_cpm_reset(uint bootpage) | ||
115 | { | ||
116 | volatile immap_t *imp; | ||
117 | volatile cpm8xx_t *commproc; | ||
118 | pte_t *pte; | ||
119 | |||
120 | imp = (immap_t *)IMAP_ADDR; | ||
121 | commproc = (cpm8xx_t *)&imp->im_cpm; | ||
122 | |||
123 | #ifdef CONFIG_UCODE_PATCH | ||
124 | /* Perform a reset. | ||
125 | */ | ||
126 | commproc->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG); | ||
127 | |||
128 | /* Wait for it. | ||
129 | */ | ||
130 | while (commproc->cp_cpcr & CPM_CR_FLG); | ||
131 | |||
132 | cpm_load_patch(imp); | ||
133 | #endif | ||
134 | |||
135 | /* Set SDMA Bus Request priority 5. | ||
136 | * On 860T, this also enables FEC priority 6. I am not sure | ||
137 | * this is what we realy want for some applications, but the | ||
138 | * manual recommends it. | ||
139 | * Bit 25, FAM can also be set to use FEC aggressive mode (860T). | ||
140 | */ | ||
141 | imp->im_siu_conf.sc_sdcr = 1; | ||
142 | |||
143 | /* Reclaim the DP memory for our use. */ | ||
144 | m8xx_cpm_dpinit(); | ||
145 | |||
146 | /* get the PTE for the bootpage */ | ||
147 | if (!get_pteptr(&init_mm, bootpage, &pte)) | ||
148 | panic("get_pteptr failed\n"); | ||
149 | |||
150 | /* and make it uncachable */ | ||
151 | pte_val(*pte) |= _PAGE_NO_CACHE; | ||
152 | _tlbie(bootpage); | ||
153 | |||
154 | host_buffer = bootpage; | ||
155 | host_end = host_buffer + PAGE_SIZE; | ||
156 | |||
157 | /* Tell everyone where the comm processor resides. | ||
158 | */ | ||
159 | cpmp = (cpm8xx_t *)commproc; | ||
160 | } | ||
161 | |||
162 | /* We used to do this earlier, but have to postpone as long as possible | ||
163 | * to ensure the kernel VM is now running. | ||
164 | */ | ||
165 | static void | ||
166 | alloc_host_memory(void) | ||
167 | { | ||
168 | dma_addr_t physaddr; | ||
169 | |||
170 | /* Set the host page for allocation. | ||
171 | */ | ||
172 | host_buffer = (uint)dma_alloc_coherent(NULL, PAGE_SIZE, &physaddr, | ||
173 | GFP_KERNEL); | ||
174 | host_end = host_buffer + PAGE_SIZE; | ||
175 | } | ||
176 | |||
177 | /* This is called during init_IRQ. We used to do it above, but this | ||
178 | * was too early since init_IRQ was not yet called. | ||
179 | */ | ||
180 | static struct irqaction cpm_error_irqaction = { | ||
181 | .handler = cpm_error_interrupt, | ||
182 | .mask = CPU_MASK_NONE, | ||
183 | }; | ||
184 | static struct irqaction cpm_interrupt_irqaction = { | ||
185 | .handler = cpm_interrupt, | ||
186 | .mask = CPU_MASK_NONE, | ||
187 | .name = "CPM cascade", | ||
188 | }; | ||
189 | |||
190 | void | ||
191 | cpm_interrupt_init(void) | ||
192 | { | ||
193 | int i; | ||
194 | |||
195 | /* Initialize the CPM interrupt controller. | ||
196 | */ | ||
197 | ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cicr = | ||
198 | (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) | | ||
199 | ((CPM_INTERRUPT/2) << 13) | CICR_HP_MASK; | ||
200 | ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr = 0; | ||
201 | |||
202 | /* install the CPM interrupt controller routines for the CPM | ||
203 | * interrupt vectors | ||
204 | */ | ||
205 | for ( i = CPM_IRQ_OFFSET ; i < CPM_IRQ_OFFSET + NR_CPM_INTS ; i++ ) | ||
206 | irq_desc[i].handler = &cpm_pic; | ||
207 | |||
208 | /* Set our interrupt handler with the core CPU. */ | ||
209 | if (setup_irq(CPM_INTERRUPT, &cpm_interrupt_irqaction)) | ||
210 | panic("Could not allocate CPM IRQ!"); | ||
211 | |||
212 | /* Install our own error handler. */ | ||
213 | cpm_error_irqaction.name = cpm_int_name[CPMVEC_ERROR]; | ||
214 | if (setup_irq(CPM_IRQ_OFFSET + CPMVEC_ERROR, &cpm_error_irqaction)) | ||
215 | panic("Could not allocate CPM error IRQ!"); | ||
216 | |||
217 | ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cicr |= CICR_IEN; | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * Get the CPM interrupt vector. | ||
222 | */ | ||
223 | int | ||
224 | cpm_get_irq(struct pt_regs *regs) | ||
225 | { | ||
226 | int cpm_vec; | ||
227 | |||
228 | /* Get the vector by setting the ACK bit and then reading | ||
229 | * the register. | ||
230 | */ | ||
231 | ((volatile immap_t *)IMAP_ADDR)->im_cpic.cpic_civr = 1; | ||
232 | cpm_vec = ((volatile immap_t *)IMAP_ADDR)->im_cpic.cpic_civr; | ||
233 | cpm_vec >>= 11; | ||
234 | |||
235 | return cpm_vec; | ||
236 | } | ||
237 | |||
238 | /* CPM interrupt controller cascade interrupt. | ||
239 | */ | ||
240 | static irqreturn_t | ||
241 | cpm_interrupt(int irq, void * dev, struct pt_regs * regs) | ||
242 | { | ||
243 | /* This interrupt handler never actually gets called. It is | ||
244 | * installed only to unmask the CPM cascade interrupt in the SIU | ||
245 | * and to make the CPM cascade interrupt visible in /proc/interrupts. | ||
246 | */ | ||
247 | return IRQ_HANDLED; | ||
248 | } | ||
249 | |||
250 | /* The CPM can generate the error interrupt when there is a race condition | ||
251 | * between generating and masking interrupts. All we have to do is ACK it | ||
252 | * and return. This is a no-op function so we don't need any special | ||
253 | * tests in the interrupt handler. | ||
254 | */ | ||
255 | static irqreturn_t | ||
256 | cpm_error_interrupt(int irq, void *dev, struct pt_regs *regs) | ||
257 | { | ||
258 | return IRQ_HANDLED; | ||
259 | } | ||
260 | |||
261 | /* A helper function to translate the handler prototype required by | ||
262 | * request_irq() to the handler prototype required by cpm_install_handler(). | ||
263 | */ | ||
264 | static irqreturn_t | ||
265 | cpm_handler_helper(int irq, void *dev_id, struct pt_regs *regs) | ||
266 | { | ||
267 | int cpm_vec = irq - CPM_IRQ_OFFSET; | ||
268 | |||
269 | (*cpm_vecs[cpm_vec].handler)(dev_id, regs); | ||
270 | |||
271 | return IRQ_HANDLED; | ||
272 | } | ||
273 | |||
274 | /* Install a CPM interrupt handler. | ||
275 | * This routine accepts a CPM interrupt vector in the range 0 to 31. | ||
276 | * This routine is retained for backward compatibility. Rather than using | ||
277 | * this routine to install a CPM interrupt handler, you can now use | ||
278 | * request_irq() with an IRQ in the range CPM_IRQ_OFFSET to | ||
279 | * CPM_IRQ_OFFSET + NR_CPM_INTS - 1 (16 to 47). | ||
280 | * | ||
281 | * Notice that the prototype of the interrupt handler function must be | ||
282 | * different depending on whether you install the handler with | ||
283 | * request_irq() or cpm_install_handler(). | ||
284 | */ | ||
285 | void | ||
286 | cpm_install_handler(int cpm_vec, void (*handler)(void *, struct pt_regs *regs), | ||
287 | void *dev_id) | ||
288 | { | ||
289 | int err; | ||
290 | |||
291 | /* If null handler, assume we are trying to free the IRQ. | ||
292 | */ | ||
293 | if (!handler) { | ||
294 | free_irq(CPM_IRQ_OFFSET + cpm_vec, dev_id); | ||
295 | return; | ||
296 | } | ||
297 | |||
298 | if (cpm_vecs[cpm_vec].handler != 0) | ||
299 | printk(KERN_INFO "CPM interrupt %x replacing %x\n", | ||
300 | (uint)handler, (uint)cpm_vecs[cpm_vec].handler); | ||
301 | cpm_vecs[cpm_vec].handler = handler; | ||
302 | cpm_vecs[cpm_vec].dev_id = dev_id; | ||
303 | |||
304 | if ((err = request_irq(CPM_IRQ_OFFSET + cpm_vec, cpm_handler_helper, | ||
305 | 0, cpm_int_name[cpm_vec], dev_id))) | ||
306 | printk(KERN_ERR "request_irq() returned %d for CPM vector %d\n", | ||
307 | err, cpm_vec); | ||
308 | } | ||
309 | |||
310 | /* Free a CPM interrupt handler. | ||
311 | * This routine accepts a CPM interrupt vector in the range 0 to 31. | ||
312 | * This routine is retained for backward compatibility. | ||
313 | */ | ||
314 | void | ||
315 | cpm_free_handler(int cpm_vec) | ||
316 | { | ||
317 | request_irq(CPM_IRQ_OFFSET + cpm_vec, NULL, 0, 0, | ||
318 | cpm_vecs[cpm_vec].dev_id); | ||
319 | |||
320 | cpm_vecs[cpm_vec].handler = NULL; | ||
321 | cpm_vecs[cpm_vec].dev_id = NULL; | ||
322 | } | ||
323 | |||
324 | /* We also own one page of host buffer space for the allocation of | ||
325 | * UART "fifos" and the like. | ||
326 | */ | ||
327 | uint | ||
328 | m8xx_cpm_hostalloc(uint size) | ||
329 | { | ||
330 | uint retloc; | ||
331 | |||
332 | if (host_buffer == 0) | ||
333 | alloc_host_memory(); | ||
334 | |||
335 | if ((host_buffer + size) >= host_end) | ||
336 | return(0); | ||
337 | |||
338 | retloc = host_buffer; | ||
339 | host_buffer += size; | ||
340 | |||
341 | return(retloc); | ||
342 | } | ||
343 | |||
344 | /* Set a baud rate generator. This needs lots of work. There are | ||
345 | * four BRGs, any of which can be wired to any channel. | ||
346 | * The internal baud rate clock is the system clock divided by 16. | ||
347 | * This assumes the baudrate is 16x oversampled by the uart. | ||
348 | */ | ||
349 | #define BRG_INT_CLK (((bd_t *)__res)->bi_intfreq) | ||
350 | #define BRG_UART_CLK (BRG_INT_CLK/16) | ||
351 | #define BRG_UART_CLK_DIV16 (BRG_UART_CLK/16) | ||
352 | |||
353 | void | ||
354 | cpm_setbrg(uint brg, uint rate) | ||
355 | { | ||
356 | volatile uint *bp; | ||
357 | |||
358 | /* This is good enough to get SMCs running..... | ||
359 | */ | ||
360 | bp = (uint *)&cpmp->cp_brgc1; | ||
361 | bp += brg; | ||
362 | /* The BRG has a 12-bit counter. For really slow baud rates (or | ||
363 | * really fast processors), we may have to further divide by 16. | ||
364 | */ | ||
365 | if (((BRG_UART_CLK / rate) - 1) < 4096) | ||
366 | *bp = (((BRG_UART_CLK / rate) - 1) << 1) | CPM_BRG_EN; | ||
367 | else | ||
368 | *bp = (((BRG_UART_CLK_DIV16 / rate) - 1) << 1) | | ||
369 | CPM_BRG_EN | CPM_BRG_DIV16; | ||
370 | } | ||
371 | |||
372 | /* | ||
373 | * dpalloc / dpfree bits. | ||
374 | */ | ||
375 | static spinlock_t cpm_dpmem_lock; | ||
376 | /* | ||
377 | * 16 blocks should be enough to satisfy all requests | ||
378 | * until the memory subsystem goes up... | ||
379 | */ | ||
380 | static rh_block_t cpm_boot_dpmem_rh_block[16]; | ||
381 | static rh_info_t cpm_dpmem_info; | ||
382 | |||
383 | #define CPM_DPMEM_ALIGNMENT 8 | ||
384 | |||
385 | void m8xx_cpm_dpinit(void) | ||
386 | { | ||
387 | cpm8xx_t *cp = &((immap_t *)IMAP_ADDR)->im_cpm; | ||
388 | |||
389 | spin_lock_init(&cpm_dpmem_lock); | ||
390 | |||
391 | /* Initialize the info header */ | ||
392 | rh_init(&cpm_dpmem_info, CPM_DPMEM_ALIGNMENT, | ||
393 | sizeof(cpm_boot_dpmem_rh_block) / | ||
394 | sizeof(cpm_boot_dpmem_rh_block[0]), | ||
395 | cpm_boot_dpmem_rh_block); | ||
396 | |||
397 | /* | ||
398 | * Attach the usable dpmem area. | ||
399 | * XXX: This is actually crap. CPM_DATAONLY_BASE and | ||
400 | * CPM_DATAONLY_SIZE are a subset of the available dparm. It varies | ||
401 | * with the processor and the microcode patches applied / activated. | ||
402 | * But the following should be at least safe. | ||
403 | */ | ||
404 | rh_attach_region(&cpm_dpmem_info, (void *)CPM_DATAONLY_BASE, CPM_DATAONLY_SIZE); | ||
405 | } | ||
406 | |||
407 | /* | ||
408 | * Allocate the requested size worth of DP memory. | ||
409 | * This function used to return an index into the DPRAM area. | ||
410 | * Now it returns the actuall physical address of that area. | ||
411 | * use m8xx_cpm_dpram_offset() to get the index | ||
412 | */ | ||
413 | uint cpm_dpalloc(uint size, uint align) | ||
414 | { | ||
415 | void *start; | ||
416 | unsigned long flags; | ||
417 | |||
418 | spin_lock_irqsave(&cpm_dpmem_lock, flags); | ||
419 | cpm_dpmem_info.alignment = align; | ||
420 | start = rh_alloc(&cpm_dpmem_info, size, "commproc"); | ||
421 | spin_unlock_irqrestore(&cpm_dpmem_lock, flags); | ||
422 | |||
423 | return (uint)start; | ||
424 | } | ||
425 | EXPORT_SYMBOL(cpm_dpalloc); | ||
426 | |||
427 | int cpm_dpfree(uint offset) | ||
428 | { | ||
429 | int ret; | ||
430 | unsigned long flags; | ||
431 | |||
432 | spin_lock_irqsave(&cpm_dpmem_lock, flags); | ||
433 | ret = rh_free(&cpm_dpmem_info, (void *)offset); | ||
434 | spin_unlock_irqrestore(&cpm_dpmem_lock, flags); | ||
435 | |||
436 | return ret; | ||
437 | } | ||
438 | EXPORT_SYMBOL(cpm_dpfree); | ||
439 | |||
440 | uint cpm_dpalloc_fixed(uint offset, uint size, uint align) | ||
441 | { | ||
442 | void *start; | ||
443 | unsigned long flags; | ||
444 | |||
445 | spin_lock_irqsave(&cpm_dpmem_lock, flags); | ||
446 | cpm_dpmem_info.alignment = align; | ||
447 | start = rh_alloc_fixed(&cpm_dpmem_info, (void *)offset, size, "commproc"); | ||
448 | spin_unlock_irqrestore(&cpm_dpmem_lock, flags); | ||
449 | |||
450 | return (uint)start; | ||
451 | } | ||
452 | EXPORT_SYMBOL(cpm_dpalloc_fixed); | ||
453 | |||
454 | void cpm_dpdump(void) | ||
455 | { | ||
456 | rh_dump(&cpm_dpmem_info); | ||
457 | } | ||
458 | EXPORT_SYMBOL(cpm_dpdump); | ||
459 | |||
460 | void *cpm_dpram_addr(uint offset) | ||
461 | { | ||
462 | return ((immap_t *)IMAP_ADDR)->im_cpm.cp_dpmem + offset; | ||
463 | } | ||
464 | EXPORT_SYMBOL(cpm_dpram_addr); | ||
diff --git a/arch/ppc/8xx_io/cs4218.h b/arch/ppc/8xx_io/cs4218.h new file mode 100644 index 000000000000..a3c38c5a5db2 --- /dev/null +++ b/arch/ppc/8xx_io/cs4218.h | |||
@@ -0,0 +1,167 @@ | |||
1 | #ifndef _cs4218_h_ | ||
2 | /* | ||
3 | * Hacked version of linux/drivers/sound/dmasound/dmasound.h | ||
4 | * | ||
5 | * | ||
6 | * Minor numbers for the sound driver. | ||
7 | * | ||
8 | * Unfortunately Creative called the codec chip of SB as a DSP. For this | ||
9 | * reason the /dev/dsp is reserved for digitized audio use. There is a | ||
10 | * device for true DSP processors but it will be called something else. | ||
11 | * In v3.0 it's /dev/sndproc but this could be a temporary solution. | ||
12 | */ | ||
13 | #define _cs4218_h_ | ||
14 | |||
15 | #include <linux/types.h> | ||
16 | #include <linux/config.h> | ||
17 | |||
18 | #define SND_NDEVS 256 /* Number of supported devices */ | ||
19 | #define SND_DEV_CTL 0 /* Control port /dev/mixer */ | ||
20 | #define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM | ||
21 | synthesizer and MIDI output) */ | ||
22 | #define SND_DEV_MIDIN 2 /* Raw midi access */ | ||
23 | #define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */ | ||
24 | #define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */ | ||
25 | #define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */ | ||
26 | #define SND_DEV_STATUS 6 /* /dev/sndstat */ | ||
27 | /* #7 not in use now. Was in 2.4. Free for use after v3.0. */ | ||
28 | #define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */ | ||
29 | #define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */ | ||
30 | #define SND_DEV_PSS SND_DEV_SNDPROC | ||
31 | |||
32 | /* switch on various prinks */ | ||
33 | #define DEBUG_DMASOUND 1 | ||
34 | |||
35 | #define MAX_AUDIO_DEV 5 | ||
36 | #define MAX_MIXER_DEV 4 | ||
37 | #define MAX_SYNTH_DEV 3 | ||
38 | #define MAX_MIDI_DEV 6 | ||
39 | #define MAX_TIMER_DEV 3 | ||
40 | |||
41 | #define MAX_CATCH_RADIUS 10 | ||
42 | |||
43 | #define le2be16(x) (((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff)) | ||
44 | #define le2be16dbl(x) (((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff)) | ||
45 | |||
46 | #define IOCTL_IN(arg, ret) \ | ||
47 | do { int error = get_user(ret, (int *)(arg)); \ | ||
48 | if (error) return error; \ | ||
49 | } while (0) | ||
50 | #define IOCTL_OUT(arg, ret) ioctl_return((int *)(arg), ret) | ||
51 | |||
52 | static inline int ioctl_return(int *addr, int value) | ||
53 | { | ||
54 | return value < 0 ? value : put_user(value, addr); | ||
55 | } | ||
56 | |||
57 | #define HAS_RECORD | ||
58 | |||
59 | /* | ||
60 | * Initialization | ||
61 | */ | ||
62 | |||
63 | /* description of the set-up applies to either hard or soft settings */ | ||
64 | |||
65 | typedef struct { | ||
66 | int format; /* AFMT_* */ | ||
67 | int stereo; /* 0 = mono, 1 = stereo */ | ||
68 | int size; /* 8/16 bit*/ | ||
69 | int speed; /* speed */ | ||
70 | } SETTINGS; | ||
71 | |||
72 | /* | ||
73 | * Machine definitions | ||
74 | */ | ||
75 | |||
76 | typedef struct { | ||
77 | const char *name; | ||
78 | const char *name2; | ||
79 | void (*open)(void); | ||
80 | void (*release)(void); | ||
81 | void *(*dma_alloc)(unsigned int, int); | ||
82 | void (*dma_free)(void *, unsigned int); | ||
83 | int (*irqinit)(void); | ||
84 | #ifdef MODULE | ||
85 | void (*irqcleanup)(void); | ||
86 | #endif | ||
87 | void (*init)(void); | ||
88 | void (*silence)(void); | ||
89 | int (*setFormat)(int); | ||
90 | int (*setVolume)(int); | ||
91 | int (*setBass)(int); | ||
92 | int (*setTreble)(int); | ||
93 | int (*setGain)(int); | ||
94 | void (*play)(void); | ||
95 | void (*record)(void); /* optional */ | ||
96 | void (*mixer_init)(void); /* optional */ | ||
97 | int (*mixer_ioctl)(u_int, u_long); /* optional */ | ||
98 | int (*write_sq_setup)(void); /* optional */ | ||
99 | int (*read_sq_setup)(void); /* optional */ | ||
100 | int (*sq_open)(mode_t); /* optional */ | ||
101 | int (*state_info)(char *, size_t); /* optional */ | ||
102 | void (*abort_read)(void); /* optional */ | ||
103 | int min_dsp_speed; | ||
104 | int max_dsp_speed; | ||
105 | int version ; | ||
106 | int hardware_afmts ; /* OSS says we only return h'ware info */ | ||
107 | /* when queried via SNDCTL_DSP_GETFMTS */ | ||
108 | int capabilities ; /* low-level reply to SNDCTL_DSP_GETCAPS */ | ||
109 | SETTINGS default_hard ; /* open() or init() should set something valid */ | ||
110 | SETTINGS default_soft ; /* you can make it look like old OSS, if you want to */ | ||
111 | } MACHINE; | ||
112 | |||
113 | /* | ||
114 | * Low level stuff | ||
115 | */ | ||
116 | |||
117 | typedef struct { | ||
118 | ssize_t (*ct_ulaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); | ||
119 | ssize_t (*ct_alaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); | ||
120 | ssize_t (*ct_s8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); | ||
121 | ssize_t (*ct_u8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); | ||
122 | ssize_t (*ct_s16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); | ||
123 | ssize_t (*ct_u16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); | ||
124 | ssize_t (*ct_s16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); | ||
125 | ssize_t (*ct_u16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); | ||
126 | } TRANS; | ||
127 | |||
128 | |||
129 | /* | ||
130 | * Sound queue stuff, the heart of the driver | ||
131 | */ | ||
132 | |||
133 | struct sound_queue { | ||
134 | /* buffers allocated for this queue */ | ||
135 | int numBufs; /* real limits on what the user can have */ | ||
136 | int bufSize; /* in bytes */ | ||
137 | char **buffers; | ||
138 | |||
139 | /* current parameters */ | ||
140 | int locked ; /* params cannot be modified when != 0 */ | ||
141 | int user_frags ; /* user requests this many */ | ||
142 | int user_frag_size ; /* of this size */ | ||
143 | int max_count; /* actual # fragments <= numBufs */ | ||
144 | int block_size; /* internal block size in bytes */ | ||
145 | int max_active; /* in-use fragments <= max_count */ | ||
146 | |||
147 | /* it shouldn't be necessary to declare any of these volatile */ | ||
148 | int front, rear, count; | ||
149 | int rear_size; | ||
150 | /* | ||
151 | * The use of the playing field depends on the hardware | ||
152 | * | ||
153 | * Atari, PMac: The number of frames that are loaded/playing | ||
154 | * | ||
155 | * Amiga: Bit 0 is set: a frame is loaded | ||
156 | * Bit 1 is set: a frame is playing | ||
157 | */ | ||
158 | int active; | ||
159 | wait_queue_head_t action_queue, open_queue, sync_queue; | ||
160 | int open_mode; | ||
161 | int busy, syncing, xruns, died; | ||
162 | }; | ||
163 | |||
164 | #define SLEEP(queue) interruptible_sleep_on_timeout(&queue, HZ) | ||
165 | #define WAKE_UP(queue) (wake_up_interruptible(&queue)) | ||
166 | |||
167 | #endif /* _cs4218_h_ */ | ||
diff --git a/arch/ppc/8xx_io/cs4218_tdm.c b/arch/ppc/8xx_io/cs4218_tdm.c new file mode 100644 index 000000000000..89fe0ceeaa40 --- /dev/null +++ b/arch/ppc/8xx_io/cs4218_tdm.c | |||
@@ -0,0 +1,2836 @@ | |||
1 | |||
2 | /* This is a modified version of linux/drivers/sound/dmasound.c to | ||
3 | * support the CS4218 codec on the 8xx TDM port. Thanks to everyone | ||
4 | * that contributed to the dmasound software (which includes me :-). | ||
5 | * | ||
6 | * The CS4218 is configured in Mode 4, sub-mode 0. This provides | ||
7 | * left/right data only on the TDM port, as a 32-bit word, per frame | ||
8 | * pulse. The control of the CS4218 is provided by some other means, | ||
9 | * like the SPI port. | ||
10 | * Dan Malek (dmalek@jlc.net) | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/timer.h> | ||
16 | #include <linux/major.h> | ||
17 | #include <linux/config.h> | ||
18 | #include <linux/fcntl.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <linux/mm.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/sound.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/delay.h> | ||
25 | |||
26 | #include <asm/system.h> | ||
27 | #include <asm/irq.h> | ||
28 | #include <asm/pgtable.h> | ||
29 | #include <asm/uaccess.h> | ||
30 | #include <asm/io.h> | ||
31 | |||
32 | /* Should probably do something different with this path name..... | ||
33 | * Actually, I should just stop using it... | ||
34 | */ | ||
35 | #include "cs4218.h" | ||
36 | #include <linux/soundcard.h> | ||
37 | |||
38 | #include <asm/mpc8xx.h> | ||
39 | #include <asm/8xx_immap.h> | ||
40 | #include <asm/commproc.h> | ||
41 | |||
42 | #define DMASND_CS4218 5 | ||
43 | |||
44 | #define MAX_CATCH_RADIUS 10 | ||
45 | #define MIN_BUFFERS 4 | ||
46 | #define MIN_BUFSIZE 4 | ||
47 | #define MAX_BUFSIZE 128 | ||
48 | |||
49 | #define HAS_8BIT_TABLES | ||
50 | |||
51 | static int sq_unit = -1; | ||
52 | static int mixer_unit = -1; | ||
53 | static int state_unit = -1; | ||
54 | static int irq_installed = 0; | ||
55 | static char **sound_buffers = NULL; | ||
56 | static char **sound_read_buffers = NULL; | ||
57 | |||
58 | static DEFINE_SPINLOCK(cs4218_lock); | ||
59 | |||
60 | /* Local copies of things we put in the control register. Output | ||
61 | * volume, like most codecs is really attenuation. | ||
62 | */ | ||
63 | static int cs4218_rate_index; | ||
64 | |||
65 | /* | ||
66 | * Stuff for outputting a beep. The values range from -327 to +327 | ||
67 | * so we can multiply by an amplitude in the range 0..100 to get a | ||
68 | * signed short value to put in the output buffer. | ||
69 | */ | ||
70 | static short beep_wform[256] = { | ||
71 | 0, 40, 79, 117, 153, 187, 218, 245, | ||
72 | 269, 288, 304, 316, 323, 327, 327, 324, | ||
73 | 318, 310, 299, 288, 275, 262, 249, 236, | ||
74 | 224, 213, 204, 196, 190, 186, 183, 182, | ||
75 | 182, 183, 186, 189, 192, 196, 200, 203, | ||
76 | 206, 208, 209, 209, 209, 207, 204, 201, | ||
77 | 197, 193, 188, 183, 179, 174, 170, 166, | ||
78 | 163, 161, 160, 159, 159, 160, 161, 162, | ||
79 | 164, 166, 168, 169, 171, 171, 171, 170, | ||
80 | 169, 167, 163, 159, 155, 150, 144, 139, | ||
81 | 133, 128, 122, 117, 113, 110, 107, 105, | ||
82 | 103, 103, 103, 103, 104, 104, 105, 105, | ||
83 | 105, 103, 101, 97, 92, 86, 78, 68, | ||
84 | 58, 45, 32, 18, 3, -11, -26, -41, | ||
85 | -55, -68, -79, -88, -95, -100, -102, -102, | ||
86 | -99, -93, -85, -75, -62, -48, -33, -16, | ||
87 | 0, 16, 33, 48, 62, 75, 85, 93, | ||
88 | 99, 102, 102, 100, 95, 88, 79, 68, | ||
89 | 55, 41, 26, 11, -3, -18, -32, -45, | ||
90 | -58, -68, -78, -86, -92, -97, -101, -103, | ||
91 | -105, -105, -105, -104, -104, -103, -103, -103, | ||
92 | -103, -105, -107, -110, -113, -117, -122, -128, | ||
93 | -133, -139, -144, -150, -155, -159, -163, -167, | ||
94 | -169, -170, -171, -171, -171, -169, -168, -166, | ||
95 | -164, -162, -161, -160, -159, -159, -160, -161, | ||
96 | -163, -166, -170, -174, -179, -183, -188, -193, | ||
97 | -197, -201, -204, -207, -209, -209, -209, -208, | ||
98 | -206, -203, -200, -196, -192, -189, -186, -183, | ||
99 | -182, -182, -183, -186, -190, -196, -204, -213, | ||
100 | -224, -236, -249, -262, -275, -288, -299, -310, | ||
101 | -318, -324, -327, -327, -323, -316, -304, -288, | ||
102 | -269, -245, -218, -187, -153, -117, -79, -40, | ||
103 | }; | ||
104 | |||
105 | #define BEEP_SPEED 5 /* 22050 Hz sample rate */ | ||
106 | #define BEEP_BUFLEN 512 | ||
107 | #define BEEP_VOLUME 15 /* 0 - 100 */ | ||
108 | |||
109 | static int beep_volume = BEEP_VOLUME; | ||
110 | static int beep_playing = 0; | ||
111 | static int beep_state = 0; | ||
112 | static short *beep_buf; | ||
113 | static void (*orig_mksound)(unsigned int, unsigned int); | ||
114 | |||
115 | /* This is found someplace else......I guess in the keyboard driver | ||
116 | * we don't include. | ||
117 | */ | ||
118 | static void (*kd_mksound)(unsigned int, unsigned int); | ||
119 | |||
120 | static int catchRadius = 0; | ||
121 | static int numBufs = 4, bufSize = 32; | ||
122 | static int numReadBufs = 4, readbufSize = 32; | ||
123 | |||
124 | |||
125 | /* TDM/Serial transmit and receive buffer descriptors. | ||
126 | */ | ||
127 | static volatile cbd_t *rx_base, *rx_cur, *tx_base, *tx_cur; | ||
128 | |||
129 | MODULE_PARM(catchRadius, "i"); | ||
130 | MODULE_PARM(numBufs, "i"); | ||
131 | MODULE_PARM(bufSize, "i"); | ||
132 | MODULE_PARM(numreadBufs, "i"); | ||
133 | MODULE_PARM(readbufSize, "i"); | ||
134 | |||
135 | #define arraysize(x) (sizeof(x)/sizeof(*(x))) | ||
136 | #define le2be16(x) (((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff)) | ||
137 | #define le2be16dbl(x) (((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff)) | ||
138 | |||
139 | #define IOCTL_IN(arg, ret) \ | ||
140 | do { int error = get_user(ret, (int *)(arg)); \ | ||
141 | if (error) return error; \ | ||
142 | } while (0) | ||
143 | #define IOCTL_OUT(arg, ret) ioctl_return((int *)(arg), ret) | ||
144 | |||
145 | /* CS4218 serial port control in mode 4. | ||
146 | */ | ||
147 | #define CS_INTMASK ((uint)0x40000000) | ||
148 | #define CS_DO1 ((uint)0x20000000) | ||
149 | #define CS_LATTEN ((uint)0x1f000000) | ||
150 | #define CS_RATTEN ((uint)0x00f80000) | ||
151 | #define CS_MUTE ((uint)0x00040000) | ||
152 | #define CS_ISL ((uint)0x00020000) | ||
153 | #define CS_ISR ((uint)0x00010000) | ||
154 | #define CS_LGAIN ((uint)0x0000f000) | ||
155 | #define CS_RGAIN ((uint)0x00000f00) | ||
156 | |||
157 | #define CS_LATTEN_SET(X) (((X) & 0x1f) << 24) | ||
158 | #define CS_RATTEN_SET(X) (((X) & 0x1f) << 19) | ||
159 | #define CS_LGAIN_SET(X) (((X) & 0x0f) << 12) | ||
160 | #define CS_RGAIN_SET(X) (((X) & 0x0f) << 8) | ||
161 | |||
162 | #define CS_LATTEN_GET(X) (((X) >> 24) & 0x1f) | ||
163 | #define CS_RATTEN_GET(X) (((X) >> 19) & 0x1f) | ||
164 | #define CS_LGAIN_GET(X) (((X) >> 12) & 0x0f) | ||
165 | #define CS_RGAIN_GET(X) (((X) >> 8) & 0x0f) | ||
166 | |||
167 | /* The control register is effectively write only. We have to keep a copy | ||
168 | * of what we write. | ||
169 | */ | ||
170 | static uint cs4218_control; | ||
171 | |||
172 | /* A place to store expanding information. | ||
173 | */ | ||
174 | static int expand_bal; | ||
175 | static int expand_data; | ||
176 | |||
177 | /* Since I can't make the microcode patch work for the SPI, I just | ||
178 | * clock the bits using software. | ||
179 | */ | ||
180 | static void sw_spi_init(void); | ||
181 | static void sw_spi_io(u_char *obuf, u_char *ibuf, uint bcnt); | ||
182 | static uint cs4218_ctl_write(uint ctlreg); | ||
183 | |||
184 | /*** Some low level helpers **************************************************/ | ||
185 | |||
186 | /* 16 bit mu-law */ | ||
187 | |||
188 | static short ulaw2dma16[] = { | ||
189 | -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, | ||
190 | -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, | ||
191 | -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, | ||
192 | -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, | ||
193 | -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, | ||
194 | -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, | ||
195 | -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, | ||
196 | -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, | ||
197 | -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, | ||
198 | -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, | ||
199 | -876, -844, -812, -780, -748, -716, -684, -652, | ||
200 | -620, -588, -556, -524, -492, -460, -428, -396, | ||
201 | -372, -356, -340, -324, -308, -292, -276, -260, | ||
202 | -244, -228, -212, -196, -180, -164, -148, -132, | ||
203 | -120, -112, -104, -96, -88, -80, -72, -64, | ||
204 | -56, -48, -40, -32, -24, -16, -8, 0, | ||
205 | 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, | ||
206 | 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, | ||
207 | 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, | ||
208 | 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, | ||
209 | 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, | ||
210 | 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, | ||
211 | 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, | ||
212 | 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, | ||
213 | 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, | ||
214 | 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, | ||
215 | 876, 844, 812, 780, 748, 716, 684, 652, | ||
216 | 620, 588, 556, 524, 492, 460, 428, 396, | ||
217 | 372, 356, 340, 324, 308, 292, 276, 260, | ||
218 | 244, 228, 212, 196, 180, 164, 148, 132, | ||
219 | 120, 112, 104, 96, 88, 80, 72, 64, | ||
220 | 56, 48, 40, 32, 24, 16, 8, 0, | ||
221 | }; | ||
222 | |||
223 | /* 16 bit A-law */ | ||
224 | |||
225 | static short alaw2dma16[] = { | ||
226 | -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, | ||
227 | -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, | ||
228 | -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, | ||
229 | -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, | ||
230 | -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944, | ||
231 | -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136, | ||
232 | -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472, | ||
233 | -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568, | ||
234 | -344, -328, -376, -360, -280, -264, -312, -296, | ||
235 | -472, -456, -504, -488, -408, -392, -440, -424, | ||
236 | -88, -72, -120, -104, -24, -8, -56, -40, | ||
237 | -216, -200, -248, -232, -152, -136, -184, -168, | ||
238 | -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, | ||
239 | -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, | ||
240 | -688, -656, -752, -720, -560, -528, -624, -592, | ||
241 | -944, -912, -1008, -976, -816, -784, -880, -848, | ||
242 | 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, | ||
243 | 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, | ||
244 | 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, | ||
245 | 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, | ||
246 | 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, | ||
247 | 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, | ||
248 | 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, | ||
249 | 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, | ||
250 | 344, 328, 376, 360, 280, 264, 312, 296, | ||
251 | 472, 456, 504, 488, 408, 392, 440, 424, | ||
252 | 88, 72, 120, 104, 24, 8, 56, 40, | ||
253 | 216, 200, 248, 232, 152, 136, 184, 168, | ||
254 | 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, | ||
255 | 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, | ||
256 | 688, 656, 752, 720, 560, 528, 624, 592, | ||
257 | 944, 912, 1008, 976, 816, 784, 880, 848, | ||
258 | }; | ||
259 | |||
260 | |||
261 | /*** Translations ************************************************************/ | ||
262 | |||
263 | |||
264 | static ssize_t cs4218_ct_law(const u_char *userPtr, size_t userCount, | ||
265 | u_char frame[], ssize_t *frameUsed, | ||
266 | ssize_t frameLeft); | ||
267 | static ssize_t cs4218_ct_s8(const u_char *userPtr, size_t userCount, | ||
268 | u_char frame[], ssize_t *frameUsed, | ||
269 | ssize_t frameLeft); | ||
270 | static ssize_t cs4218_ct_u8(const u_char *userPtr, size_t userCount, | ||
271 | u_char frame[], ssize_t *frameUsed, | ||
272 | ssize_t frameLeft); | ||
273 | static ssize_t cs4218_ct_s16(const u_char *userPtr, size_t userCount, | ||
274 | u_char frame[], ssize_t *frameUsed, | ||
275 | ssize_t frameLeft); | ||
276 | static ssize_t cs4218_ct_u16(const u_char *userPtr, size_t userCount, | ||
277 | u_char frame[], ssize_t *frameUsed, | ||
278 | ssize_t frameLeft); | ||
279 | static ssize_t cs4218_ctx_law(const u_char *userPtr, size_t userCount, | ||
280 | u_char frame[], ssize_t *frameUsed, | ||
281 | ssize_t frameLeft); | ||
282 | static ssize_t cs4218_ctx_s8(const u_char *userPtr, size_t userCount, | ||
283 | u_char frame[], ssize_t *frameUsed, | ||
284 | ssize_t frameLeft); | ||
285 | static ssize_t cs4218_ctx_u8(const u_char *userPtr, size_t userCount, | ||
286 | u_char frame[], ssize_t *frameUsed, | ||
287 | ssize_t frameLeft); | ||
288 | static ssize_t cs4218_ctx_s16(const u_char *userPtr, size_t userCount, | ||
289 | u_char frame[], ssize_t *frameUsed, | ||
290 | ssize_t frameLeft); | ||
291 | static ssize_t cs4218_ctx_u16(const u_char *userPtr, size_t userCount, | ||
292 | u_char frame[], ssize_t *frameUsed, | ||
293 | ssize_t frameLeft); | ||
294 | static ssize_t cs4218_ct_s16_read(const u_char *userPtr, size_t userCount, | ||
295 | u_char frame[], ssize_t *frameUsed, | ||
296 | ssize_t frameLeft); | ||
297 | static ssize_t cs4218_ct_u16_read(const u_char *userPtr, size_t userCount, | ||
298 | u_char frame[], ssize_t *frameUsed, | ||
299 | ssize_t frameLeft); | ||
300 | |||
301 | |||
302 | /*** Low level stuff *********************************************************/ | ||
303 | |||
304 | struct cs_sound_settings { | ||
305 | MACHINE mach; /* machine dependent things */ | ||
306 | SETTINGS hard; /* hardware settings */ | ||
307 | SETTINGS soft; /* software settings */ | ||
308 | SETTINGS dsp; /* /dev/dsp default settings */ | ||
309 | TRANS *trans_write; /* supported translations for playback */ | ||
310 | TRANS *trans_read; /* supported translations for record */ | ||
311 | int volume_left; /* volume (range is machine dependent) */ | ||
312 | int volume_right; | ||
313 | int bass; /* tone (range is machine dependent) */ | ||
314 | int treble; | ||
315 | int gain; | ||
316 | int minDev; /* minor device number currently open */ | ||
317 | }; | ||
318 | |||
319 | static struct cs_sound_settings sound; | ||
320 | |||
321 | static void *CS_Alloc(unsigned int size, int flags); | ||
322 | static void CS_Free(void *ptr, unsigned int size); | ||
323 | static int CS_IrqInit(void); | ||
324 | #ifdef MODULE | ||
325 | static void CS_IrqCleanup(void); | ||
326 | #endif /* MODULE */ | ||
327 | static void CS_Silence(void); | ||
328 | static void CS_Init(void); | ||
329 | static void CS_Play(void); | ||
330 | static void CS_Record(void); | ||
331 | static int CS_SetFormat(int format); | ||
332 | static int CS_SetVolume(int volume); | ||
333 | static void cs4218_tdm_tx_intr(void *devid); | ||
334 | static void cs4218_tdm_rx_intr(void *devid); | ||
335 | static void cs4218_intr(void *devid, struct pt_regs *regs); | ||
336 | static int cs_get_volume(uint reg); | ||
337 | static int cs_volume_setter(int volume, int mute); | ||
338 | static int cs_get_gain(uint reg); | ||
339 | static int cs_set_gain(int gain); | ||
340 | static void cs_mksound(unsigned int hz, unsigned int ticks); | ||
341 | static void cs_nosound(unsigned long xx); | ||
342 | |||
343 | /*** Mid level stuff *********************************************************/ | ||
344 | |||
345 | |||
346 | static void sound_silence(void); | ||
347 | static void sound_init(void); | ||
348 | static int sound_set_format(int format); | ||
349 | static int sound_set_speed(int speed); | ||
350 | static int sound_set_stereo(int stereo); | ||
351 | static int sound_set_volume(int volume); | ||
352 | |||
353 | static ssize_t sound_copy_translate(const u_char *userPtr, | ||
354 | size_t userCount, | ||
355 | u_char frame[], ssize_t *frameUsed, | ||
356 | ssize_t frameLeft); | ||
357 | static ssize_t sound_copy_translate_read(const u_char *userPtr, | ||
358 | size_t userCount, | ||
359 | u_char frame[], ssize_t *frameUsed, | ||
360 | ssize_t frameLeft); | ||
361 | |||
362 | |||
363 | /* | ||
364 | * /dev/mixer abstraction | ||
365 | */ | ||
366 | |||
367 | struct sound_mixer { | ||
368 | int busy; | ||
369 | int modify_counter; | ||
370 | }; | ||
371 | |||
372 | static struct sound_mixer mixer; | ||
373 | |||
374 | static struct sound_queue sq; | ||
375 | static struct sound_queue read_sq; | ||
376 | |||
377 | #define sq_block_address(i) (sq.buffers[i]) | ||
378 | #define SIGNAL_RECEIVED (signal_pending(current)) | ||
379 | #define NON_BLOCKING(open_mode) (open_mode & O_NONBLOCK) | ||
380 | #define ONE_SECOND HZ /* in jiffies (100ths of a second) */ | ||
381 | #define NO_TIME_LIMIT 0xffffffff | ||
382 | |||
383 | /* | ||
384 | * /dev/sndstat | ||
385 | */ | ||
386 | |||
387 | struct sound_state { | ||
388 | int busy; | ||
389 | char buf[512]; | ||
390 | int len, ptr; | ||
391 | }; | ||
392 | |||
393 | static struct sound_state state; | ||
394 | |||
395 | /*** Common stuff ********************************************************/ | ||
396 | |||
397 | static long long sound_lseek(struct file *file, long long offset, int orig); | ||
398 | |||
399 | /*** Config & Setup **********************************************************/ | ||
400 | |||
401 | void dmasound_setup(char *str, int *ints); | ||
402 | |||
403 | /*** Translations ************************************************************/ | ||
404 | |||
405 | |||
406 | /* ++TeSche: radically changed for new expanding purposes... | ||
407 | * | ||
408 | * These two routines now deal with copying/expanding/translating the samples | ||
409 | * from user space into our buffer at the right frequency. They take care about | ||
410 | * how much data there's actually to read, how much buffer space there is and | ||
411 | * to convert samples into the right frequency/encoding. They will only work on | ||
412 | * complete samples so it may happen they leave some bytes in the input stream | ||
413 | * if the user didn't write a multiple of the current sample size. They both | ||
414 | * return the number of bytes they've used from both streams so you may detect | ||
415 | * such a situation. Luckily all programs should be able to cope with that. | ||
416 | * | ||
417 | * I think I've optimized anything as far as one can do in plain C, all | ||
418 | * variables should fit in registers and the loops are really short. There's | ||
419 | * one loop for every possible situation. Writing a more generalized and thus | ||
420 | * parameterized loop would only produce slower code. Feel free to optimize | ||
421 | * this in assembler if you like. :) | ||
422 | * | ||
423 | * I think these routines belong here because they're not yet really hardware | ||
424 | * independent, especially the fact that the Falcon can play 16bit samples | ||
425 | * only in stereo is hardcoded in both of them! | ||
426 | * | ||
427 | * ++geert: split in even more functions (one per format) | ||
428 | */ | ||
429 | |||
430 | static ssize_t cs4218_ct_law(const u_char *userPtr, size_t userCount, | ||
431 | u_char frame[], ssize_t *frameUsed, | ||
432 | ssize_t frameLeft) | ||
433 | { | ||
434 | short *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma16: alaw2dma16; | ||
435 | ssize_t count, used; | ||
436 | short *p = (short *) &frame[*frameUsed]; | ||
437 | int val, stereo = sound.soft.stereo; | ||
438 | |||
439 | frameLeft >>= 2; | ||
440 | if (stereo) | ||
441 | userCount >>= 1; | ||
442 | used = count = min(userCount, frameLeft); | ||
443 | while (count > 0) { | ||
444 | u_char data; | ||
445 | if (get_user(data, userPtr++)) | ||
446 | return -EFAULT; | ||
447 | val = table[data]; | ||
448 | *p++ = val; | ||
449 | if (stereo) { | ||
450 | if (get_user(data, userPtr++)) | ||
451 | return -EFAULT; | ||
452 | val = table[data]; | ||
453 | } | ||
454 | *p++ = val; | ||
455 | count--; | ||
456 | } | ||
457 | *frameUsed += used * 4; | ||
458 | return stereo? used * 2: used; | ||
459 | } | ||
460 | |||
461 | |||
462 | static ssize_t cs4218_ct_s8(const u_char *userPtr, size_t userCount, | ||
463 | u_char frame[], ssize_t *frameUsed, | ||
464 | ssize_t frameLeft) | ||
465 | { | ||
466 | ssize_t count, used; | ||
467 | short *p = (short *) &frame[*frameUsed]; | ||
468 | int val, stereo = sound.soft.stereo; | ||
469 | |||
470 | frameLeft >>= 2; | ||
471 | if (stereo) | ||
472 | userCount >>= 1; | ||
473 | used = count = min(userCount, frameLeft); | ||
474 | while (count > 0) { | ||
475 | u_char data; | ||
476 | if (get_user(data, userPtr++)) | ||
477 | return -EFAULT; | ||
478 | val = data << 8; | ||
479 | *p++ = val; | ||
480 | if (stereo) { | ||
481 | if (get_user(data, userPtr++)) | ||
482 | return -EFAULT; | ||
483 | val = data << 8; | ||
484 | } | ||
485 | *p++ = val; | ||
486 | count--; | ||
487 | } | ||
488 | *frameUsed += used * 4; | ||
489 | return stereo? used * 2: used; | ||
490 | } | ||
491 | |||
492 | |||
493 | static ssize_t cs4218_ct_u8(const u_char *userPtr, size_t userCount, | ||
494 | u_char frame[], ssize_t *frameUsed, | ||
495 | ssize_t frameLeft) | ||
496 | { | ||
497 | ssize_t count, used; | ||
498 | short *p = (short *) &frame[*frameUsed]; | ||
499 | int val, stereo = sound.soft.stereo; | ||
500 | |||
501 | frameLeft >>= 2; | ||
502 | if (stereo) | ||
503 | userCount >>= 1; | ||
504 | used = count = min(userCount, frameLeft); | ||
505 | while (count > 0) { | ||
506 | u_char data; | ||
507 | if (get_user(data, userPtr++)) | ||
508 | return -EFAULT; | ||
509 | val = (data ^ 0x80) << 8; | ||
510 | *p++ = val; | ||
511 | if (stereo) { | ||
512 | if (get_user(data, userPtr++)) | ||
513 | return -EFAULT; | ||
514 | val = (data ^ 0x80) << 8; | ||
515 | } | ||
516 | *p++ = val; | ||
517 | count--; | ||
518 | } | ||
519 | *frameUsed += used * 4; | ||
520 | return stereo? used * 2: used; | ||
521 | } | ||
522 | |||
523 | |||
524 | /* This is the default format of the codec. Signed, 16-bit stereo | ||
525 | * generated by an application shouldn't have to be copied at all. | ||
526 | * We should just get the phsical address of the buffers and update | ||
527 | * the TDM BDs directly. | ||
528 | */ | ||
529 | static ssize_t cs4218_ct_s16(const u_char *userPtr, size_t userCount, | ||
530 | u_char frame[], ssize_t *frameUsed, | ||
531 | ssize_t frameLeft) | ||
532 | { | ||
533 | ssize_t count, used; | ||
534 | int stereo = sound.soft.stereo; | ||
535 | short *fp = (short *) &frame[*frameUsed]; | ||
536 | |||
537 | frameLeft >>= 2; | ||
538 | userCount >>= (stereo? 2: 1); | ||
539 | used = count = min(userCount, frameLeft); | ||
540 | if (!stereo) { | ||
541 | short *up = (short *) userPtr; | ||
542 | while (count > 0) { | ||
543 | short data; | ||
544 | if (get_user(data, up++)) | ||
545 | return -EFAULT; | ||
546 | *fp++ = data; | ||
547 | *fp++ = data; | ||
548 | count--; | ||
549 | } | ||
550 | } else { | ||
551 | if (copy_from_user(fp, userPtr, count * 4)) | ||
552 | return -EFAULT; | ||
553 | } | ||
554 | *frameUsed += used * 4; | ||
555 | return stereo? used * 4: used * 2; | ||
556 | } | ||
557 | |||
558 | static ssize_t cs4218_ct_u16(const u_char *userPtr, size_t userCount, | ||
559 | u_char frame[], ssize_t *frameUsed, | ||
560 | ssize_t frameLeft) | ||
561 | { | ||
562 | ssize_t count, used; | ||
563 | int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); | ||
564 | int stereo = sound.soft.stereo; | ||
565 | short *fp = (short *) &frame[*frameUsed]; | ||
566 | short *up = (short *) userPtr; | ||
567 | |||
568 | frameLeft >>= 2; | ||
569 | userCount >>= (stereo? 2: 1); | ||
570 | used = count = min(userCount, frameLeft); | ||
571 | while (count > 0) { | ||
572 | int data; | ||
573 | if (get_user(data, up++)) | ||
574 | return -EFAULT; | ||
575 | data ^= mask; | ||
576 | *fp++ = data; | ||
577 | if (stereo) { | ||
578 | if (get_user(data, up++)) | ||
579 | return -EFAULT; | ||
580 | data ^= mask; | ||
581 | } | ||
582 | *fp++ = data; | ||
583 | count--; | ||
584 | } | ||
585 | *frameUsed += used * 4; | ||
586 | return stereo? used * 4: used * 2; | ||
587 | } | ||
588 | |||
589 | |||
590 | static ssize_t cs4218_ctx_law(const u_char *userPtr, size_t userCount, | ||
591 | u_char frame[], ssize_t *frameUsed, | ||
592 | ssize_t frameLeft) | ||
593 | { | ||
594 | unsigned short *table = (unsigned short *) | ||
595 | (sound.soft.format == AFMT_MU_LAW ? ulaw2dma16: alaw2dma16); | ||
596 | unsigned int data = expand_data; | ||
597 | unsigned int *p = (unsigned int *) &frame[*frameUsed]; | ||
598 | int bal = expand_bal; | ||
599 | int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; | ||
600 | int utotal, ftotal; | ||
601 | int stereo = sound.soft.stereo; | ||
602 | |||
603 | frameLeft >>= 2; | ||
604 | if (stereo) | ||
605 | userCount >>= 1; | ||
606 | ftotal = frameLeft; | ||
607 | utotal = userCount; | ||
608 | while (frameLeft) { | ||
609 | u_char c; | ||
610 | if (bal < 0) { | ||
611 | if (userCount == 0) | ||
612 | break; | ||
613 | if (get_user(c, userPtr++)) | ||
614 | return -EFAULT; | ||
615 | data = table[c]; | ||
616 | if (stereo) { | ||
617 | if (get_user(c, userPtr++)) | ||
618 | return -EFAULT; | ||
619 | data = (data << 16) + table[c]; | ||
620 | } else | ||
621 | data = (data << 16) + data; | ||
622 | userCount--; | ||
623 | bal += hSpeed; | ||
624 | } | ||
625 | *p++ = data; | ||
626 | frameLeft--; | ||
627 | bal -= sSpeed; | ||
628 | } | ||
629 | expand_bal = bal; | ||
630 | expand_data = data; | ||
631 | *frameUsed += (ftotal - frameLeft) * 4; | ||
632 | utotal -= userCount; | ||
633 | return stereo? utotal * 2: utotal; | ||
634 | } | ||
635 | |||
636 | |||
637 | static ssize_t cs4218_ctx_s8(const u_char *userPtr, size_t userCount, | ||
638 | u_char frame[], ssize_t *frameUsed, | ||
639 | ssize_t frameLeft) | ||
640 | { | ||
641 | unsigned int *p = (unsigned int *) &frame[*frameUsed]; | ||
642 | unsigned int data = expand_data; | ||
643 | int bal = expand_bal; | ||
644 | int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; | ||
645 | int stereo = sound.soft.stereo; | ||
646 | int utotal, ftotal; | ||
647 | |||
648 | frameLeft >>= 2; | ||
649 | if (stereo) | ||
650 | userCount >>= 1; | ||
651 | ftotal = frameLeft; | ||
652 | utotal = userCount; | ||
653 | while (frameLeft) { | ||
654 | u_char c; | ||
655 | if (bal < 0) { | ||
656 | if (userCount == 0) | ||
657 | break; | ||
658 | if (get_user(c, userPtr++)) | ||
659 | return -EFAULT; | ||
660 | data = c << 8; | ||
661 | if (stereo) { | ||
662 | if (get_user(c, userPtr++)) | ||
663 | return -EFAULT; | ||
664 | data = (data << 16) + (c << 8); | ||
665 | } else | ||
666 | data = (data << 16) + data; | ||
667 | userCount--; | ||
668 | bal += hSpeed; | ||
669 | } | ||
670 | *p++ = data; | ||
671 | frameLeft--; | ||
672 | bal -= sSpeed; | ||
673 | } | ||
674 | expand_bal = bal; | ||
675 | expand_data = data; | ||
676 | *frameUsed += (ftotal - frameLeft) * 4; | ||
677 | utotal -= userCount; | ||
678 | return stereo? utotal * 2: utotal; | ||
679 | } | ||
680 | |||
681 | |||
682 | static ssize_t cs4218_ctx_u8(const u_char *userPtr, size_t userCount, | ||
683 | u_char frame[], ssize_t *frameUsed, | ||
684 | ssize_t frameLeft) | ||
685 | { | ||
686 | unsigned int *p = (unsigned int *) &frame[*frameUsed]; | ||
687 | unsigned int data = expand_data; | ||
688 | int bal = expand_bal; | ||
689 | int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; | ||
690 | int stereo = sound.soft.stereo; | ||
691 | int utotal, ftotal; | ||
692 | |||
693 | frameLeft >>= 2; | ||
694 | if (stereo) | ||
695 | userCount >>= 1; | ||
696 | ftotal = frameLeft; | ||
697 | utotal = userCount; | ||
698 | while (frameLeft) { | ||
699 | u_char c; | ||
700 | if (bal < 0) { | ||
701 | if (userCount == 0) | ||
702 | break; | ||
703 | if (get_user(c, userPtr++)) | ||
704 | return -EFAULT; | ||
705 | data = (c ^ 0x80) << 8; | ||
706 | if (stereo) { | ||
707 | if (get_user(c, userPtr++)) | ||
708 | return -EFAULT; | ||
709 | data = (data << 16) + ((c ^ 0x80) << 8); | ||
710 | } else | ||
711 | data = (data << 16) + data; | ||
712 | userCount--; | ||
713 | bal += hSpeed; | ||
714 | } | ||
715 | *p++ = data; | ||
716 | frameLeft--; | ||
717 | bal -= sSpeed; | ||
718 | } | ||
719 | expand_bal = bal; | ||
720 | expand_data = data; | ||
721 | *frameUsed += (ftotal - frameLeft) * 4; | ||
722 | utotal -= userCount; | ||
723 | return stereo? utotal * 2: utotal; | ||
724 | } | ||
725 | |||
726 | |||
727 | static ssize_t cs4218_ctx_s16(const u_char *userPtr, size_t userCount, | ||
728 | u_char frame[], ssize_t *frameUsed, | ||
729 | ssize_t frameLeft) | ||
730 | { | ||
731 | unsigned int *p = (unsigned int *) &frame[*frameUsed]; | ||
732 | unsigned int data = expand_data; | ||
733 | unsigned short *up = (unsigned short *) userPtr; | ||
734 | int bal = expand_bal; | ||
735 | int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; | ||
736 | int stereo = sound.soft.stereo; | ||
737 | int utotal, ftotal; | ||
738 | |||
739 | frameLeft >>= 2; | ||
740 | userCount >>= (stereo? 2: 1); | ||
741 | ftotal = frameLeft; | ||
742 | utotal = userCount; | ||
743 | while (frameLeft) { | ||
744 | unsigned short c; | ||
745 | if (bal < 0) { | ||
746 | if (userCount == 0) | ||
747 | break; | ||
748 | if (get_user(data, up++)) | ||
749 | return -EFAULT; | ||
750 | if (stereo) { | ||
751 | if (get_user(c, up++)) | ||
752 | return -EFAULT; | ||
753 | data = (data << 16) + c; | ||
754 | } else | ||
755 | data = (data << 16) + data; | ||
756 | userCount--; | ||
757 | bal += hSpeed; | ||
758 | } | ||
759 | *p++ = data; | ||
760 | frameLeft--; | ||
761 | bal -= sSpeed; | ||
762 | } | ||
763 | expand_bal = bal; | ||
764 | expand_data = data; | ||
765 | *frameUsed += (ftotal - frameLeft) * 4; | ||
766 | utotal -= userCount; | ||
767 | return stereo? utotal * 4: utotal * 2; | ||
768 | } | ||
769 | |||
770 | |||
771 | static ssize_t cs4218_ctx_u16(const u_char *userPtr, size_t userCount, | ||
772 | u_char frame[], ssize_t *frameUsed, | ||
773 | ssize_t frameLeft) | ||
774 | { | ||
775 | int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); | ||
776 | unsigned int *p = (unsigned int *) &frame[*frameUsed]; | ||
777 | unsigned int data = expand_data; | ||
778 | unsigned short *up = (unsigned short *) userPtr; | ||
779 | int bal = expand_bal; | ||
780 | int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; | ||
781 | int stereo = sound.soft.stereo; | ||
782 | int utotal, ftotal; | ||
783 | |||
784 | frameLeft >>= 2; | ||
785 | userCount >>= (stereo? 2: 1); | ||
786 | ftotal = frameLeft; | ||
787 | utotal = userCount; | ||
788 | while (frameLeft) { | ||
789 | unsigned short c; | ||
790 | if (bal < 0) { | ||
791 | if (userCount == 0) | ||
792 | break; | ||
793 | if (get_user(data, up++)) | ||
794 | return -EFAULT; | ||
795 | data ^= mask; | ||
796 | if (stereo) { | ||
797 | if (get_user(c, up++)) | ||
798 | return -EFAULT; | ||
799 | data = (data << 16) + (c ^ mask); | ||
800 | } else | ||
801 | data = (data << 16) + data; | ||
802 | userCount--; | ||
803 | bal += hSpeed; | ||
804 | } | ||
805 | *p++ = data; | ||
806 | frameLeft--; | ||
807 | bal -= sSpeed; | ||
808 | } | ||
809 | expand_bal = bal; | ||
810 | expand_data = data; | ||
811 | *frameUsed += (ftotal - frameLeft) * 4; | ||
812 | utotal -= userCount; | ||
813 | return stereo? utotal * 4: utotal * 2; | ||
814 | } | ||
815 | |||
816 | static ssize_t cs4218_ct_s8_read(const u_char *userPtr, size_t userCount, | ||
817 | u_char frame[], ssize_t *frameUsed, | ||
818 | ssize_t frameLeft) | ||
819 | { | ||
820 | ssize_t count, used; | ||
821 | short *p = (short *) &frame[*frameUsed]; | ||
822 | int val, stereo = sound.soft.stereo; | ||
823 | |||
824 | frameLeft >>= 2; | ||
825 | if (stereo) | ||
826 | userCount >>= 1; | ||
827 | used = count = min(userCount, frameLeft); | ||
828 | while (count > 0) { | ||
829 | u_char data; | ||
830 | |||
831 | val = *p++; | ||
832 | data = val >> 8; | ||
833 | if (put_user(data, (u_char *)userPtr++)) | ||
834 | return -EFAULT; | ||
835 | if (stereo) { | ||
836 | val = *p; | ||
837 | data = val >> 8; | ||
838 | if (put_user(data, (u_char *)userPtr++)) | ||
839 | return -EFAULT; | ||
840 | } | ||
841 | p++; | ||
842 | count--; | ||
843 | } | ||
844 | *frameUsed += used * 4; | ||
845 | return stereo? used * 2: used; | ||
846 | } | ||
847 | |||
848 | |||
849 | static ssize_t cs4218_ct_u8_read(const u_char *userPtr, size_t userCount, | ||
850 | u_char frame[], ssize_t *frameUsed, | ||
851 | ssize_t frameLeft) | ||
852 | { | ||
853 | ssize_t count, used; | ||
854 | short *p = (short *) &frame[*frameUsed]; | ||
855 | int val, stereo = sound.soft.stereo; | ||
856 | |||
857 | frameLeft >>= 2; | ||
858 | if (stereo) | ||
859 | userCount >>= 1; | ||
860 | used = count = min(userCount, frameLeft); | ||
861 | while (count > 0) { | ||
862 | u_char data; | ||
863 | |||
864 | val = *p++; | ||
865 | data = (val >> 8) ^ 0x80; | ||
866 | if (put_user(data, (u_char *)userPtr++)) | ||
867 | return -EFAULT; | ||
868 | if (stereo) { | ||
869 | val = *p; | ||
870 | data = (val >> 8) ^ 0x80; | ||
871 | if (put_user(data, (u_char *)userPtr++)) | ||
872 | return -EFAULT; | ||
873 | } | ||
874 | p++; | ||
875 | count--; | ||
876 | } | ||
877 | *frameUsed += used * 4; | ||
878 | return stereo? used * 2: used; | ||
879 | } | ||
880 | |||
881 | |||
882 | static ssize_t cs4218_ct_s16_read(const u_char *userPtr, size_t userCount, | ||
883 | u_char frame[], ssize_t *frameUsed, | ||
884 | ssize_t frameLeft) | ||
885 | { | ||
886 | ssize_t count, used; | ||
887 | int stereo = sound.soft.stereo; | ||
888 | short *fp = (short *) &frame[*frameUsed]; | ||
889 | |||
890 | frameLeft >>= 2; | ||
891 | userCount >>= (stereo? 2: 1); | ||
892 | used = count = min(userCount, frameLeft); | ||
893 | if (!stereo) { | ||
894 | short *up = (short *) userPtr; | ||
895 | while (count > 0) { | ||
896 | short data; | ||
897 | data = *fp; | ||
898 | if (put_user(data, up++)) | ||
899 | return -EFAULT; | ||
900 | fp+=2; | ||
901 | count--; | ||
902 | } | ||
903 | } else { | ||
904 | if (copy_to_user((u_char *)userPtr, fp, count * 4)) | ||
905 | return -EFAULT; | ||
906 | } | ||
907 | *frameUsed += used * 4; | ||
908 | return stereo? used * 4: used * 2; | ||
909 | } | ||
910 | |||
911 | static ssize_t cs4218_ct_u16_read(const u_char *userPtr, size_t userCount, | ||
912 | u_char frame[], ssize_t *frameUsed, | ||
913 | ssize_t frameLeft) | ||
914 | { | ||
915 | ssize_t count, used; | ||
916 | int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); | ||
917 | int stereo = sound.soft.stereo; | ||
918 | short *fp = (short *) &frame[*frameUsed]; | ||
919 | short *up = (short *) userPtr; | ||
920 | |||
921 | frameLeft >>= 2; | ||
922 | userCount >>= (stereo? 2: 1); | ||
923 | used = count = min(userCount, frameLeft); | ||
924 | while (count > 0) { | ||
925 | int data; | ||
926 | |||
927 | data = *fp++; | ||
928 | data ^= mask; | ||
929 | if (put_user(data, up++)) | ||
930 | return -EFAULT; | ||
931 | if (stereo) { | ||
932 | data = *fp; | ||
933 | data ^= mask; | ||
934 | if (put_user(data, up++)) | ||
935 | return -EFAULT; | ||
936 | } | ||
937 | fp++; | ||
938 | count--; | ||
939 | } | ||
940 | *frameUsed += used * 4; | ||
941 | return stereo? used * 4: used * 2; | ||
942 | } | ||
943 | |||
944 | static TRANS transCSNormal = { | ||
945 | cs4218_ct_law, cs4218_ct_law, cs4218_ct_s8, cs4218_ct_u8, | ||
946 | cs4218_ct_s16, cs4218_ct_u16, cs4218_ct_s16, cs4218_ct_u16 | ||
947 | }; | ||
948 | |||
949 | static TRANS transCSExpand = { | ||
950 | cs4218_ctx_law, cs4218_ctx_law, cs4218_ctx_s8, cs4218_ctx_u8, | ||
951 | cs4218_ctx_s16, cs4218_ctx_u16, cs4218_ctx_s16, cs4218_ctx_u16 | ||
952 | }; | ||
953 | |||
954 | static TRANS transCSNormalRead = { | ||
955 | NULL, NULL, cs4218_ct_s8_read, cs4218_ct_u8_read, | ||
956 | cs4218_ct_s16_read, cs4218_ct_u16_read, | ||
957 | cs4218_ct_s16_read, cs4218_ct_u16_read | ||
958 | }; | ||
959 | |||
960 | /*** Low level stuff *********************************************************/ | ||
961 | |||
962 | static void *CS_Alloc(unsigned int size, int flags) | ||
963 | { | ||
964 | int order; | ||
965 | |||
966 | size >>= 13; | ||
967 | for (order=0; order < 5; order++) { | ||
968 | if (size == 0) | ||
969 | break; | ||
970 | size >>= 1; | ||
971 | } | ||
972 | return (void *)__get_free_pages(flags, order); | ||
973 | } | ||
974 | |||
975 | static void CS_Free(void *ptr, unsigned int size) | ||
976 | { | ||
977 | int order; | ||
978 | |||
979 | size >>= 13; | ||
980 | for (order=0; order < 5; order++) { | ||
981 | if (size == 0) | ||
982 | break; | ||
983 | size >>= 1; | ||
984 | } | ||
985 | free_pages((ulong)ptr, order); | ||
986 | } | ||
987 | |||
988 | static int __init CS_IrqInit(void) | ||
989 | { | ||
990 | cpm_install_handler(CPMVEC_SMC2, cs4218_intr, NULL); | ||
991 | return 1; | ||
992 | } | ||
993 | |||
994 | #ifdef MODULE | ||
995 | static void CS_IrqCleanup(void) | ||
996 | { | ||
997 | volatile smc_t *sp; | ||
998 | volatile cpm8xx_t *cp; | ||
999 | |||
1000 | /* First disable transmitter and receiver. | ||
1001 | */ | ||
1002 | sp = &cpmp->cp_smc[1]; | ||
1003 | sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); | ||
1004 | |||
1005 | /* And now shut down the SMC. | ||
1006 | */ | ||
1007 | cp = cpmp; /* Get pointer to Communication Processor */ | ||
1008 | cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, | ||
1009 | CPM_CR_STOP_TX) | CPM_CR_FLG; | ||
1010 | while (cp->cp_cpcr & CPM_CR_FLG); | ||
1011 | |||
1012 | /* Release the interrupt handler. | ||
1013 | */ | ||
1014 | cpm_free_handler(CPMVEC_SMC2); | ||
1015 | |||
1016 | if (beep_buf) | ||
1017 | kfree(beep_buf); | ||
1018 | kd_mksound = orig_mksound; | ||
1019 | } | ||
1020 | #endif /* MODULE */ | ||
1021 | |||
1022 | static void CS_Silence(void) | ||
1023 | { | ||
1024 | volatile smc_t *sp; | ||
1025 | |||
1026 | /* Disable transmitter. | ||
1027 | */ | ||
1028 | sp = &cpmp->cp_smc[1]; | ||
1029 | sp->smc_smcmr &= ~SMCMR_TEN; | ||
1030 | } | ||
1031 | |||
1032 | /* Frequencies depend upon external oscillator. There are two | ||
1033 | * choices, 12.288 and 11.2896 MHz. The RPCG audio supports both through | ||
1034 | * and external control register selection bit. | ||
1035 | */ | ||
1036 | static int cs4218_freqs[] = { | ||
1037 | /* 12.288 11.2896 */ | ||
1038 | 48000, 44100, | ||
1039 | 32000, 29400, | ||
1040 | 24000, 22050, | ||
1041 | 19200, 17640, | ||
1042 | 16000, 14700, | ||
1043 | 12000, 11025, | ||
1044 | 9600, 8820, | ||
1045 | 8000, 7350 | ||
1046 | }; | ||
1047 | |||
1048 | static void CS_Init(void) | ||
1049 | { | ||
1050 | int i, tolerance; | ||
1051 | |||
1052 | switch (sound.soft.format) { | ||
1053 | case AFMT_S16_LE: | ||
1054 | case AFMT_U16_LE: | ||
1055 | sound.hard.format = AFMT_S16_LE; | ||
1056 | break; | ||
1057 | default: | ||
1058 | sound.hard.format = AFMT_S16_BE; | ||
1059 | break; | ||
1060 | } | ||
1061 | sound.hard.stereo = 1; | ||
1062 | sound.hard.size = 16; | ||
1063 | |||
1064 | /* | ||
1065 | * If we have a sample rate which is within catchRadius percent | ||
1066 | * of the requested value, we don't have to expand the samples. | ||
1067 | * Otherwise choose the next higher rate. | ||
1068 | */ | ||
1069 | i = (sizeof(cs4218_freqs) / sizeof(int)); | ||
1070 | do { | ||
1071 | tolerance = catchRadius * cs4218_freqs[--i] / 100; | ||
1072 | } while (sound.soft.speed > cs4218_freqs[i] + tolerance && i > 0); | ||
1073 | if (sound.soft.speed >= cs4218_freqs[i] - tolerance) | ||
1074 | sound.trans_write = &transCSNormal; | ||
1075 | else | ||
1076 | sound.trans_write = &transCSExpand; | ||
1077 | sound.trans_read = &transCSNormalRead; | ||
1078 | sound.hard.speed = cs4218_freqs[i]; | ||
1079 | cs4218_rate_index = i; | ||
1080 | |||
1081 | /* The CS4218 has seven selectable clock dividers for the sample | ||
1082 | * clock. The HIOX then provides one of two external rates. | ||
1083 | * An even numbered frequency table index uses the high external | ||
1084 | * clock rate. | ||
1085 | */ | ||
1086 | *(uint *)HIOX_CSR4_ADDR &= ~(HIOX_CSR4_AUDCLKHI | HIOX_CSR4_AUDCLKSEL); | ||
1087 | if ((i & 1) == 0) | ||
1088 | *(uint *)HIOX_CSR4_ADDR |= HIOX_CSR4_AUDCLKHI; | ||
1089 | i >>= 1; | ||
1090 | *(uint *)HIOX_CSR4_ADDR |= (i & HIOX_CSR4_AUDCLKSEL); | ||
1091 | |||
1092 | expand_bal = -sound.soft.speed; | ||
1093 | } | ||
1094 | |||
1095 | static int CS_SetFormat(int format) | ||
1096 | { | ||
1097 | int size; | ||
1098 | |||
1099 | switch (format) { | ||
1100 | case AFMT_QUERY: | ||
1101 | return sound.soft.format; | ||
1102 | case AFMT_MU_LAW: | ||
1103 | case AFMT_A_LAW: | ||
1104 | case AFMT_U8: | ||
1105 | case AFMT_S8: | ||
1106 | size = 8; | ||
1107 | break; | ||
1108 | case AFMT_S16_BE: | ||
1109 | case AFMT_U16_BE: | ||
1110 | case AFMT_S16_LE: | ||
1111 | case AFMT_U16_LE: | ||
1112 | size = 16; | ||
1113 | break; | ||
1114 | default: /* :-) */ | ||
1115 | printk(KERN_ERR "dmasound: unknown format 0x%x, using AFMT_U8\n", | ||
1116 | format); | ||
1117 | size = 8; | ||
1118 | format = AFMT_U8; | ||
1119 | } | ||
1120 | |||
1121 | sound.soft.format = format; | ||
1122 | sound.soft.size = size; | ||
1123 | if (sound.minDev == SND_DEV_DSP) { | ||
1124 | sound.dsp.format = format; | ||
1125 | sound.dsp.size = size; | ||
1126 | } | ||
1127 | |||
1128 | CS_Init(); | ||
1129 | |||
1130 | return format; | ||
1131 | } | ||
1132 | |||
1133 | /* Volume is the amount of attenuation we tell the codec to impose | ||
1134 | * on the outputs. There are 32 levels, with 0 the "loudest". | ||
1135 | */ | ||
1136 | #define CS_VOLUME_TO_MASK(x) (31 - ((((x) - 1) * 31) / 99)) | ||
1137 | #define CS_MASK_TO_VOLUME(y) (100 - ((y) * 99 / 31)) | ||
1138 | |||
1139 | static int cs_get_volume(uint reg) | ||
1140 | { | ||
1141 | int volume; | ||
1142 | |||
1143 | volume = CS_MASK_TO_VOLUME(CS_LATTEN_GET(reg)); | ||
1144 | volume |= CS_MASK_TO_VOLUME(CS_RATTEN_GET(reg)) << 8; | ||
1145 | return volume; | ||
1146 | } | ||
1147 | |||
1148 | static int cs_volume_setter(int volume, int mute) | ||
1149 | { | ||
1150 | uint tempctl; | ||
1151 | |||
1152 | if (mute && volume == 0) { | ||
1153 | tempctl = cs4218_control | CS_MUTE; | ||
1154 | } else { | ||
1155 | tempctl = cs4218_control & ~CS_MUTE; | ||
1156 | tempctl = tempctl & ~(CS_LATTEN | CS_RATTEN); | ||
1157 | tempctl |= CS_LATTEN_SET(CS_VOLUME_TO_MASK(volume & 0xff)); | ||
1158 | tempctl |= CS_RATTEN_SET(CS_VOLUME_TO_MASK((volume >> 8) & 0xff)); | ||
1159 | volume = cs_get_volume(tempctl); | ||
1160 | } | ||
1161 | if (tempctl != cs4218_control) { | ||
1162 | cs4218_ctl_write(tempctl); | ||
1163 | } | ||
1164 | return volume; | ||
1165 | } | ||
1166 | |||
1167 | |||
1168 | /* Gain has 16 steps from 0 to 15. These are in 1.5dB increments from | ||
1169 | * 0 (no gain) to 22.5 dB. | ||
1170 | */ | ||
1171 | #define CS_RECLEVEL_TO_GAIN(v) \ | ||
1172 | ((v) < 0 ? 0 : (v) > 100 ? 15 : (v) * 3 / 20) | ||
1173 | #define CS_GAIN_TO_RECLEVEL(v) (((v) * 20 + 2) / 3) | ||
1174 | |||
1175 | static int cs_get_gain(uint reg) | ||
1176 | { | ||
1177 | int gain; | ||
1178 | |||
1179 | gain = CS_GAIN_TO_RECLEVEL(CS_LGAIN_GET(reg)); | ||
1180 | gain |= CS_GAIN_TO_RECLEVEL(CS_RGAIN_GET(reg)) << 8; | ||
1181 | return gain; | ||
1182 | } | ||
1183 | |||
1184 | static int cs_set_gain(int gain) | ||
1185 | { | ||
1186 | uint tempctl; | ||
1187 | |||
1188 | tempctl = cs4218_control & ~(CS_LGAIN | CS_RGAIN); | ||
1189 | tempctl |= CS_LGAIN_SET(CS_RECLEVEL_TO_GAIN(gain & 0xff)); | ||
1190 | tempctl |= CS_RGAIN_SET(CS_RECLEVEL_TO_GAIN((gain >> 8) & 0xff)); | ||
1191 | gain = cs_get_gain(tempctl); | ||
1192 | |||
1193 | if (tempctl != cs4218_control) { | ||
1194 | cs4218_ctl_write(tempctl); | ||
1195 | } | ||
1196 | return gain; | ||
1197 | } | ||
1198 | |||
1199 | static int CS_SetVolume(int volume) | ||
1200 | { | ||
1201 | return cs_volume_setter(volume, CS_MUTE); | ||
1202 | } | ||
1203 | |||
1204 | static void CS_Play(void) | ||
1205 | { | ||
1206 | int i, count; | ||
1207 | unsigned long flags; | ||
1208 | volatile cbd_t *bdp; | ||
1209 | volatile cpm8xx_t *cp; | ||
1210 | |||
1211 | /* Protect buffer */ | ||
1212 | spin_lock_irqsave(&cs4218_lock, flags); | ||
1213 | #if 0 | ||
1214 | if (awacs_beep_state) { | ||
1215 | /* sound takes precedence over beeps */ | ||
1216 | out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); | ||
1217 | out_le32(&awacs->control, | ||
1218 | (in_le32(&awacs->control) & ~0x1f00) | ||
1219 | | (awacs_rate_index << 8)); | ||
1220 | out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE); | ||
1221 | out_le32(&awacs_txdma->cmdptr, virt_to_bus(&(awacs_tx_cmds[(sq.front+sq.active) % sq.max_count]))); | ||
1222 | |||
1223 | beep_playing = 0; | ||
1224 | awacs_beep_state = 0; | ||
1225 | } | ||
1226 | #endif | ||
1227 | i = sq.front + sq.active; | ||
1228 | if (i >= sq.max_count) | ||
1229 | i -= sq.max_count; | ||
1230 | while (sq.active < 2 && sq.active < sq.count) { | ||
1231 | count = (sq.count == sq.active + 1)?sq.rear_size:sq.block_size; | ||
1232 | if (count < sq.block_size && !sq.syncing) | ||
1233 | /* last block not yet filled, and we're not syncing. */ | ||
1234 | break; | ||
1235 | |||
1236 | bdp = &tx_base[i]; | ||
1237 | bdp->cbd_datlen = count; | ||
1238 | |||
1239 | flush_dcache_range((ulong)sound_buffers[i], | ||
1240 | (ulong)(sound_buffers[i] + count)); | ||
1241 | |||
1242 | if (++i >= sq.max_count) | ||
1243 | i = 0; | ||
1244 | |||
1245 | if (sq.active == 0) { | ||
1246 | /* The SMC does not load its fifo until the first | ||
1247 | * TDM frame pulse, so the transmit data gets shifted | ||
1248 | * by one word. To compensate for this, we incorrectly | ||
1249 | * transmit the first buffer and shorten it by one | ||
1250 | * word. Subsequent buffers are then aligned properly. | ||
1251 | */ | ||
1252 | bdp->cbd_datlen -= 2; | ||
1253 | |||
1254 | /* Start up the SMC Transmitter. | ||
1255 | */ | ||
1256 | cp = cpmp; | ||
1257 | cp->cp_smc[1].smc_smcmr |= SMCMR_TEN; | ||
1258 | cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, | ||
1259 | CPM_CR_RESTART_TX) | CPM_CR_FLG; | ||
1260 | while (cp->cp_cpcr & CPM_CR_FLG); | ||
1261 | } | ||
1262 | |||
1263 | /* Buffer is ready now. | ||
1264 | */ | ||
1265 | bdp->cbd_sc |= BD_SC_READY; | ||
1266 | |||
1267 | ++sq.active; | ||
1268 | } | ||
1269 | spin_unlock_irqrestore(&cs4218_lock, flags); | ||
1270 | } | ||
1271 | |||
1272 | |||
1273 | static void CS_Record(void) | ||
1274 | { | ||
1275 | unsigned long flags; | ||
1276 | volatile smc_t *sp; | ||
1277 | |||
1278 | if (read_sq.active) | ||
1279 | return; | ||
1280 | |||
1281 | /* Protect buffer */ | ||
1282 | spin_lock_irqsave(&cs4218_lock, flags); | ||
1283 | |||
1284 | /* This is all we have to do......Just start it up. | ||
1285 | */ | ||
1286 | sp = &cpmp->cp_smc[1]; | ||
1287 | sp->smc_smcmr |= SMCMR_REN; | ||
1288 | |||
1289 | read_sq.active = 1; | ||
1290 | |||
1291 | spin_unlock_irqrestore(&cs4218_lock, flags); | ||
1292 | } | ||
1293 | |||
1294 | |||
1295 | static void | ||
1296 | cs4218_tdm_tx_intr(void *devid) | ||
1297 | { | ||
1298 | int i = sq.front; | ||
1299 | volatile cbd_t *bdp; | ||
1300 | |||
1301 | while (sq.active > 0) { | ||
1302 | bdp = &tx_base[i]; | ||
1303 | if (bdp->cbd_sc & BD_SC_READY) | ||
1304 | break; /* this frame is still going */ | ||
1305 | --sq.count; | ||
1306 | --sq.active; | ||
1307 | if (++i >= sq.max_count) | ||
1308 | i = 0; | ||
1309 | } | ||
1310 | if (i != sq.front) | ||
1311 | WAKE_UP(sq.action_queue); | ||
1312 | sq.front = i; | ||
1313 | |||
1314 | CS_Play(); | ||
1315 | |||
1316 | if (!sq.active) | ||
1317 | WAKE_UP(sq.sync_queue); | ||
1318 | } | ||
1319 | |||
1320 | |||
1321 | static void | ||
1322 | cs4218_tdm_rx_intr(void *devid) | ||
1323 | { | ||
1324 | |||
1325 | /* We want to blow 'em off when shutting down. | ||
1326 | */ | ||
1327 | if (read_sq.active == 0) | ||
1328 | return; | ||
1329 | |||
1330 | /* Check multiple buffers in case we were held off from | ||
1331 | * interrupt processing for a long time. Geeze, I really hope | ||
1332 | * this doesn't happen. | ||
1333 | */ | ||
1334 | while ((rx_base[read_sq.rear].cbd_sc & BD_SC_EMPTY) == 0) { | ||
1335 | |||
1336 | /* Invalidate the data cache range for this buffer. | ||
1337 | */ | ||
1338 | invalidate_dcache_range( | ||
1339 | (uint)(sound_read_buffers[read_sq.rear]), | ||
1340 | (uint)(sound_read_buffers[read_sq.rear] + read_sq.block_size)); | ||
1341 | |||
1342 | /* Make buffer available again and move on. | ||
1343 | */ | ||
1344 | rx_base[read_sq.rear].cbd_sc |= BD_SC_EMPTY; | ||
1345 | read_sq.rear++; | ||
1346 | |||
1347 | /* Wrap the buffer ring. | ||
1348 | */ | ||
1349 | if (read_sq.rear >= read_sq.max_active) | ||
1350 | read_sq.rear = 0; | ||
1351 | |||
1352 | /* If we have caught up to the front buffer, bump it. | ||
1353 | * This will cause weird (but not fatal) results if the | ||
1354 | * read loop is currently using this buffer. The user is | ||
1355 | * behind in this case anyway, so weird things are going | ||
1356 | * to happen. | ||
1357 | */ | ||
1358 | if (read_sq.rear == read_sq.front) { | ||
1359 | read_sq.front++; | ||
1360 | if (read_sq.front >= read_sq.max_active) | ||
1361 | read_sq.front = 0; | ||
1362 | } | ||
1363 | } | ||
1364 | |||
1365 | WAKE_UP(read_sq.action_queue); | ||
1366 | } | ||
1367 | |||
1368 | static void cs_nosound(unsigned long xx) | ||
1369 | { | ||
1370 | unsigned long flags; | ||
1371 | |||
1372 | /* not sure if this is needed, since hardware command is #if 0'd */ | ||
1373 | spin_lock_irqsave(&cs4218_lock, flags); | ||
1374 | if (beep_playing) { | ||
1375 | #if 0 | ||
1376 | st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); | ||
1377 | #endif | ||
1378 | beep_playing = 0; | ||
1379 | } | ||
1380 | spin_unlock_irqrestore(&cs4218_lock, flags); | ||
1381 | } | ||
1382 | |||
1383 | static struct timer_list beep_timer = TIMER_INITIALIZER(cs_nosound, 0, 0); | ||
1384 | }; | ||
1385 | |||
1386 | static void cs_mksound(unsigned int hz, unsigned int ticks) | ||
1387 | { | ||
1388 | unsigned long flags; | ||
1389 | int beep_speed = BEEP_SPEED; | ||
1390 | int srate = cs4218_freqs[beep_speed]; | ||
1391 | int period, ncycles, nsamples; | ||
1392 | int i, j, f; | ||
1393 | short *p; | ||
1394 | static int beep_hz_cache; | ||
1395 | static int beep_nsamples_cache; | ||
1396 | static int beep_volume_cache; | ||
1397 | |||
1398 | if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) { | ||
1399 | #if 1 | ||
1400 | /* this is a hack for broken X server code */ | ||
1401 | hz = 750; | ||
1402 | ticks = 12; | ||
1403 | #else | ||
1404 | /* cancel beep currently playing */ | ||
1405 | awacs_nosound(0); | ||
1406 | return; | ||
1407 | #endif | ||
1408 | } | ||
1409 | /* lock while modifying beep_timer */ | ||
1410 | spin_lock_irqsave(&cs4218_lock, flags); | ||
1411 | del_timer(&beep_timer); | ||
1412 | if (ticks) { | ||
1413 | beep_timer.expires = jiffies + ticks; | ||
1414 | add_timer(&beep_timer); | ||
1415 | } | ||
1416 | if (beep_playing || sq.active || beep_buf == NULL) { | ||
1417 | spin_unlock_irqrestore(&cs4218_lock, flags); | ||
1418 | return; /* too hard, sorry :-( */ | ||
1419 | } | ||
1420 | beep_playing = 1; | ||
1421 | #if 0 | ||
1422 | st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS); | ||
1423 | #endif | ||
1424 | spin_unlock_irqrestore(&cs4218_lock, flags); | ||
1425 | |||
1426 | if (hz == beep_hz_cache && beep_volume == beep_volume_cache) { | ||
1427 | nsamples = beep_nsamples_cache; | ||
1428 | } else { | ||
1429 | period = srate * 256 / hz; /* fixed point */ | ||
1430 | ncycles = BEEP_BUFLEN * 256 / period; | ||
1431 | nsamples = (period * ncycles) >> 8; | ||
1432 | f = ncycles * 65536 / nsamples; | ||
1433 | j = 0; | ||
1434 | p = beep_buf; | ||
1435 | for (i = 0; i < nsamples; ++i, p += 2) { | ||
1436 | p[0] = p[1] = beep_wform[j >> 8] * beep_volume; | ||
1437 | j = (j + f) & 0xffff; | ||
1438 | } | ||
1439 | beep_hz_cache = hz; | ||
1440 | beep_volume_cache = beep_volume; | ||
1441 | beep_nsamples_cache = nsamples; | ||
1442 | } | ||
1443 | |||
1444 | #if 0 | ||
1445 | st_le16(&beep_dbdma_cmd->req_count, nsamples*4); | ||
1446 | st_le16(&beep_dbdma_cmd->xfer_status, 0); | ||
1447 | st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd)); | ||
1448 | st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf)); | ||
1449 | awacs_beep_state = 1; | ||
1450 | |||
1451 | spin_lock_irqsave(&cs4218_lock, flags); | ||
1452 | if (beep_playing) { /* i.e. haven't been terminated already */ | ||
1453 | out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); | ||
1454 | out_le32(&awacs->control, | ||
1455 | (in_le32(&awacs->control) & ~0x1f00) | ||
1456 | | (beep_speed << 8)); | ||
1457 | out_le32(&awacs->byteswap, 0); | ||
1458 | out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); | ||
1459 | out_le32(&awacs_txdma->control, RUN | (RUN << 16)); | ||
1460 | } | ||
1461 | spin_unlock_irqrestore(&cs4218_lock, flags); | ||
1462 | #endif | ||
1463 | } | ||
1464 | |||
1465 | static MACHINE mach_cs4218 = { | ||
1466 | .owner = THIS_MODULE, | ||
1467 | .name = "HIOX CS4218", | ||
1468 | .name2 = "Built-in Sound", | ||
1469 | .dma_alloc = CS_Alloc, | ||
1470 | .dma_free = CS_Free, | ||
1471 | .irqinit = CS_IrqInit, | ||
1472 | #ifdef MODULE | ||
1473 | .irqcleanup = CS_IrqCleanup, | ||
1474 | #endif /* MODULE */ | ||
1475 | .init = CS_Init, | ||
1476 | .silence = CS_Silence, | ||
1477 | .setFormat = CS_SetFormat, | ||
1478 | .setVolume = CS_SetVolume, | ||
1479 | .play = CS_Play | ||
1480 | }; | ||
1481 | |||
1482 | |||
1483 | /*** Mid level stuff *********************************************************/ | ||
1484 | |||
1485 | |||
1486 | static void sound_silence(void) | ||
1487 | { | ||
1488 | /* update hardware settings one more */ | ||
1489 | (*sound.mach.init)(); | ||
1490 | |||
1491 | (*sound.mach.silence)(); | ||
1492 | } | ||
1493 | |||
1494 | |||
1495 | static void sound_init(void) | ||
1496 | { | ||
1497 | (*sound.mach.init)(); | ||
1498 | } | ||
1499 | |||
1500 | |||
1501 | static int sound_set_format(int format) | ||
1502 | { | ||
1503 | return(*sound.mach.setFormat)(format); | ||
1504 | } | ||
1505 | |||
1506 | |||
1507 | static int sound_set_speed(int speed) | ||
1508 | { | ||
1509 | if (speed < 0) | ||
1510 | return(sound.soft.speed); | ||
1511 | |||
1512 | sound.soft.speed = speed; | ||
1513 | (*sound.mach.init)(); | ||
1514 | if (sound.minDev == SND_DEV_DSP) | ||
1515 | sound.dsp.speed = sound.soft.speed; | ||
1516 | |||
1517 | return(sound.soft.speed); | ||
1518 | } | ||
1519 | |||
1520 | |||
1521 | static int sound_set_stereo(int stereo) | ||
1522 | { | ||
1523 | if (stereo < 0) | ||
1524 | return(sound.soft.stereo); | ||
1525 | |||
1526 | stereo = !!stereo; /* should be 0 or 1 now */ | ||
1527 | |||
1528 | sound.soft.stereo = stereo; | ||
1529 | if (sound.minDev == SND_DEV_DSP) | ||
1530 | sound.dsp.stereo = stereo; | ||
1531 | (*sound.mach.init)(); | ||
1532 | |||
1533 | return(stereo); | ||
1534 | } | ||
1535 | |||
1536 | |||
1537 | static int sound_set_volume(int volume) | ||
1538 | { | ||
1539 | return(*sound.mach.setVolume)(volume); | ||
1540 | } | ||
1541 | |||
1542 | static ssize_t sound_copy_translate(const u_char *userPtr, | ||
1543 | size_t userCount, | ||
1544 | u_char frame[], ssize_t *frameUsed, | ||
1545 | ssize_t frameLeft) | ||
1546 | { | ||
1547 | ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t) = NULL; | ||
1548 | |||
1549 | switch (sound.soft.format) { | ||
1550 | case AFMT_MU_LAW: | ||
1551 | ct_func = sound.trans_write->ct_ulaw; | ||
1552 | break; | ||
1553 | case AFMT_A_LAW: | ||
1554 | ct_func = sound.trans_write->ct_alaw; | ||
1555 | break; | ||
1556 | case AFMT_S8: | ||
1557 | ct_func = sound.trans_write->ct_s8; | ||
1558 | break; | ||
1559 | case AFMT_U8: | ||
1560 | ct_func = sound.trans_write->ct_u8; | ||
1561 | break; | ||
1562 | case AFMT_S16_BE: | ||
1563 | ct_func = sound.trans_write->ct_s16be; | ||
1564 | break; | ||
1565 | case AFMT_U16_BE: | ||
1566 | ct_func = sound.trans_write->ct_u16be; | ||
1567 | break; | ||
1568 | case AFMT_S16_LE: | ||
1569 | ct_func = sound.trans_write->ct_s16le; | ||
1570 | break; | ||
1571 | case AFMT_U16_LE: | ||
1572 | ct_func = sound.trans_write->ct_u16le; | ||
1573 | break; | ||
1574 | } | ||
1575 | if (ct_func) | ||
1576 | return ct_func(userPtr, userCount, frame, frameUsed, frameLeft); | ||
1577 | else | ||
1578 | return 0; | ||
1579 | } | ||
1580 | |||
1581 | static ssize_t sound_copy_translate_read(const u_char *userPtr, | ||
1582 | size_t userCount, | ||
1583 | u_char frame[], ssize_t *frameUsed, | ||
1584 | ssize_t frameLeft) | ||
1585 | { | ||
1586 | ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t) = NULL; | ||
1587 | |||
1588 | switch (sound.soft.format) { | ||
1589 | case AFMT_MU_LAW: | ||
1590 | ct_func = sound.trans_read->ct_ulaw; | ||
1591 | break; | ||
1592 | case AFMT_A_LAW: | ||
1593 | ct_func = sound.trans_read->ct_alaw; | ||
1594 | break; | ||
1595 | case AFMT_S8: | ||
1596 | ct_func = sound.trans_read->ct_s8; | ||
1597 | break; | ||
1598 | case AFMT_U8: | ||
1599 | ct_func = sound.trans_read->ct_u8; | ||
1600 | break; | ||
1601 | case AFMT_S16_BE: | ||
1602 | ct_func = sound.trans_read->ct_s16be; | ||
1603 | break; | ||
1604 | case AFMT_U16_BE: | ||
1605 | ct_func = sound.trans_read->ct_u16be; | ||
1606 | break; | ||
1607 | case AFMT_S16_LE: | ||
1608 | ct_func = sound.trans_read->ct_s16le; | ||
1609 | break; | ||
1610 | case AFMT_U16_LE: | ||
1611 | ct_func = sound.trans_read->ct_u16le; | ||
1612 | break; | ||
1613 | } | ||
1614 | if (ct_func) | ||
1615 | return ct_func(userPtr, userCount, frame, frameUsed, frameLeft); | ||
1616 | else | ||
1617 | return 0; | ||
1618 | } | ||
1619 | |||
1620 | |||
1621 | /* | ||
1622 | * /dev/mixer abstraction | ||
1623 | */ | ||
1624 | |||
1625 | static int mixer_open(struct inode *inode, struct file *file) | ||
1626 | { | ||
1627 | mixer.busy = 1; | ||
1628 | return nonseekable_open(inode, file); | ||
1629 | } | ||
1630 | |||
1631 | |||
1632 | static int mixer_release(struct inode *inode, struct file *file) | ||
1633 | { | ||
1634 | mixer.busy = 0; | ||
1635 | return 0; | ||
1636 | } | ||
1637 | |||
1638 | |||
1639 | static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, | ||
1640 | u_long arg) | ||
1641 | { | ||
1642 | int data; | ||
1643 | uint tmpcs; | ||
1644 | |||
1645 | if (_SIOC_DIR(cmd) & _SIOC_WRITE) | ||
1646 | mixer.modify_counter++; | ||
1647 | if (cmd == OSS_GETVERSION) | ||
1648 | return IOCTL_OUT(arg, SOUND_VERSION); | ||
1649 | switch (cmd) { | ||
1650 | case SOUND_MIXER_INFO: { | ||
1651 | mixer_info info; | ||
1652 | strlcpy(info.id, "CS4218_TDM", sizeof(info.id)); | ||
1653 | strlcpy(info.name, "CS4218_TDM", sizeof(info.name)); | ||
1654 | info.name[sizeof(info.name)-1] = 0; | ||
1655 | info.modify_counter = mixer.modify_counter; | ||
1656 | if (copy_to_user((int *)arg, &info, sizeof(info))) | ||
1657 | return -EFAULT; | ||
1658 | return 0; | ||
1659 | } | ||
1660 | case SOUND_MIXER_READ_DEVMASK: | ||
1661 | data = SOUND_MASK_VOLUME | SOUND_MASK_LINE | ||
1662 | | SOUND_MASK_MIC | SOUND_MASK_RECLEV | ||
1663 | | SOUND_MASK_ALTPCM; | ||
1664 | return IOCTL_OUT(arg, data); | ||
1665 | case SOUND_MIXER_READ_RECMASK: | ||
1666 | data = SOUND_MASK_LINE | SOUND_MASK_MIC; | ||
1667 | return IOCTL_OUT(arg, data); | ||
1668 | case SOUND_MIXER_READ_RECSRC: | ||
1669 | if (cs4218_control & CS_DO1) | ||
1670 | data = SOUND_MASK_LINE; | ||
1671 | else | ||
1672 | data = SOUND_MASK_MIC; | ||
1673 | return IOCTL_OUT(arg, data); | ||
1674 | case SOUND_MIXER_WRITE_RECSRC: | ||
1675 | IOCTL_IN(arg, data); | ||
1676 | data &= (SOUND_MASK_LINE | SOUND_MASK_MIC); | ||
1677 | if (data & SOUND_MASK_LINE) | ||
1678 | tmpcs = cs4218_control | | ||
1679 | (CS_ISL | CS_ISR | CS_DO1); | ||
1680 | if (data & SOUND_MASK_MIC) | ||
1681 | tmpcs = cs4218_control & | ||
1682 | ~(CS_ISL | CS_ISR | CS_DO1); | ||
1683 | if (tmpcs != cs4218_control) | ||
1684 | cs4218_ctl_write(tmpcs); | ||
1685 | return IOCTL_OUT(arg, data); | ||
1686 | case SOUND_MIXER_READ_STEREODEVS: | ||
1687 | data = SOUND_MASK_VOLUME | SOUND_MASK_RECLEV; | ||
1688 | return IOCTL_OUT(arg, data); | ||
1689 | case SOUND_MIXER_READ_CAPS: | ||
1690 | return IOCTL_OUT(arg, 0); | ||
1691 | case SOUND_MIXER_READ_VOLUME: | ||
1692 | data = (cs4218_control & CS_MUTE)? 0: | ||
1693 | cs_get_volume(cs4218_control); | ||
1694 | return IOCTL_OUT(arg, data); | ||
1695 | case SOUND_MIXER_WRITE_VOLUME: | ||
1696 | IOCTL_IN(arg, data); | ||
1697 | return IOCTL_OUT(arg, sound_set_volume(data)); | ||
1698 | case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ | ||
1699 | IOCTL_IN(arg, data); | ||
1700 | beep_volume = data & 0xff; | ||
1701 | /* fall through */ | ||
1702 | case SOUND_MIXER_READ_ALTPCM: | ||
1703 | return IOCTL_OUT(arg, beep_volume); | ||
1704 | case SOUND_MIXER_WRITE_RECLEV: | ||
1705 | IOCTL_IN(arg, data); | ||
1706 | data = cs_set_gain(data); | ||
1707 | return IOCTL_OUT(arg, data); | ||
1708 | case SOUND_MIXER_READ_RECLEV: | ||
1709 | data = cs_get_gain(cs4218_control); | ||
1710 | return IOCTL_OUT(arg, data); | ||
1711 | } | ||
1712 | |||
1713 | return -EINVAL; | ||
1714 | } | ||
1715 | |||
1716 | |||
1717 | static struct file_operations mixer_fops = | ||
1718 | { | ||
1719 | .owner = THIS_MODULE, | ||
1720 | .llseek = sound_lseek, | ||
1721 | .ioctl = mixer_ioctl, | ||
1722 | .open = mixer_open, | ||
1723 | .release = mixer_release, | ||
1724 | }; | ||
1725 | |||
1726 | |||
1727 | static void __init mixer_init(void) | ||
1728 | { | ||
1729 | mixer_unit = register_sound_mixer(&mixer_fops, -1); | ||
1730 | if (mixer_unit < 0) | ||
1731 | return; | ||
1732 | |||
1733 | mixer.busy = 0; | ||
1734 | sound.treble = 0; | ||
1735 | sound.bass = 0; | ||
1736 | |||
1737 | /* Set Line input, no gain, no attenuation. | ||
1738 | */ | ||
1739 | cs4218_control = CS_ISL | CS_ISR | CS_DO1; | ||
1740 | cs4218_control |= CS_LGAIN_SET(0) | CS_RGAIN_SET(0); | ||
1741 | cs4218_control |= CS_LATTEN_SET(0) | CS_RATTEN_SET(0); | ||
1742 | cs4218_ctl_write(cs4218_control); | ||
1743 | } | ||
1744 | |||
1745 | |||
1746 | /* | ||
1747 | * Sound queue stuff, the heart of the driver | ||
1748 | */ | ||
1749 | |||
1750 | |||
1751 | static int sq_allocate_buffers(void) | ||
1752 | { | ||
1753 | int i; | ||
1754 | |||
1755 | if (sound_buffers) | ||
1756 | return 0; | ||
1757 | sound_buffers = kmalloc (numBufs * sizeof(char *), GFP_KERNEL); | ||
1758 | if (!sound_buffers) | ||
1759 | return -ENOMEM; | ||
1760 | for (i = 0; i < numBufs; i++) { | ||
1761 | sound_buffers[i] = sound.mach.dma_alloc (bufSize << 10, GFP_KERNEL); | ||
1762 | if (!sound_buffers[i]) { | ||
1763 | while (i--) | ||
1764 | sound.mach.dma_free (sound_buffers[i], bufSize << 10); | ||
1765 | kfree (sound_buffers); | ||
1766 | sound_buffers = 0; | ||
1767 | return -ENOMEM; | ||
1768 | } | ||
1769 | } | ||
1770 | return 0; | ||
1771 | } | ||
1772 | |||
1773 | |||
1774 | static void sq_release_buffers(void) | ||
1775 | { | ||
1776 | int i; | ||
1777 | |||
1778 | if (sound_buffers) { | ||
1779 | for (i = 0; i < numBufs; i++) | ||
1780 | sound.mach.dma_free (sound_buffers[i], bufSize << 10); | ||
1781 | kfree (sound_buffers); | ||
1782 | sound_buffers = 0; | ||
1783 | } | ||
1784 | } | ||
1785 | |||
1786 | |||
1787 | static int sq_allocate_read_buffers(void) | ||
1788 | { | ||
1789 | int i; | ||
1790 | |||
1791 | if (sound_read_buffers) | ||
1792 | return 0; | ||
1793 | sound_read_buffers = kmalloc(numReadBufs * sizeof(char *), GFP_KERNEL); | ||
1794 | if (!sound_read_buffers) | ||
1795 | return -ENOMEM; | ||
1796 | for (i = 0; i < numBufs; i++) { | ||
1797 | sound_read_buffers[i] = sound.mach.dma_alloc (readbufSize<<10, | ||
1798 | GFP_KERNEL); | ||
1799 | if (!sound_read_buffers[i]) { | ||
1800 | while (i--) | ||
1801 | sound.mach.dma_free (sound_read_buffers[i], | ||
1802 | readbufSize << 10); | ||
1803 | kfree (sound_read_buffers); | ||
1804 | sound_read_buffers = 0; | ||
1805 | return -ENOMEM; | ||
1806 | } | ||
1807 | } | ||
1808 | return 0; | ||
1809 | } | ||
1810 | |||
1811 | static void sq_release_read_buffers(void) | ||
1812 | { | ||
1813 | int i; | ||
1814 | |||
1815 | if (sound_read_buffers) { | ||
1816 | cpmp->cp_smc[1].smc_smcmr &= ~SMCMR_REN; | ||
1817 | for (i = 0; i < numReadBufs; i++) | ||
1818 | sound.mach.dma_free (sound_read_buffers[i], | ||
1819 | bufSize << 10); | ||
1820 | kfree (sound_read_buffers); | ||
1821 | sound_read_buffers = 0; | ||
1822 | } | ||
1823 | } | ||
1824 | |||
1825 | |||
1826 | static void sq_setup(int numBufs, int bufSize, char **write_buffers) | ||
1827 | { | ||
1828 | int i; | ||
1829 | volatile cbd_t *bdp; | ||
1830 | volatile cpm8xx_t *cp; | ||
1831 | volatile smc_t *sp; | ||
1832 | |||
1833 | /* Make sure the SMC transmit is shut down. | ||
1834 | */ | ||
1835 | cp = cpmp; | ||
1836 | sp = &cpmp->cp_smc[1]; | ||
1837 | sp->smc_smcmr &= ~SMCMR_TEN; | ||
1838 | |||
1839 | sq.max_count = numBufs; | ||
1840 | sq.max_active = numBufs; | ||
1841 | sq.block_size = bufSize; | ||
1842 | sq.buffers = write_buffers; | ||
1843 | |||
1844 | sq.front = sq.count = 0; | ||
1845 | sq.rear = -1; | ||
1846 | sq.syncing = 0; | ||
1847 | sq.active = 0; | ||
1848 | |||
1849 | bdp = tx_base; | ||
1850 | for (i=0; i<numBufs; i++) { | ||
1851 | bdp->cbd_bufaddr = virt_to_bus(write_buffers[i]); | ||
1852 | bdp++; | ||
1853 | } | ||
1854 | |||
1855 | /* This causes the SMC to sync up with the first buffer again. | ||
1856 | */ | ||
1857 | cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, CPM_CR_INIT_TX) | CPM_CR_FLG; | ||
1858 | while (cp->cp_cpcr & CPM_CR_FLG); | ||
1859 | } | ||
1860 | |||
1861 | static void read_sq_setup(int numBufs, int bufSize, char **read_buffers) | ||
1862 | { | ||
1863 | int i; | ||
1864 | volatile cbd_t *bdp; | ||
1865 | volatile cpm8xx_t *cp; | ||
1866 | volatile smc_t *sp; | ||
1867 | |||
1868 | /* Make sure the SMC receive is shut down. | ||
1869 | */ | ||
1870 | cp = cpmp; | ||
1871 | sp = &cpmp->cp_smc[1]; | ||
1872 | sp->smc_smcmr &= ~SMCMR_REN; | ||
1873 | |||
1874 | read_sq.max_count = numBufs; | ||
1875 | read_sq.max_active = numBufs; | ||
1876 | read_sq.block_size = bufSize; | ||
1877 | read_sq.buffers = read_buffers; | ||
1878 | |||
1879 | read_sq.front = read_sq.count = 0; | ||
1880 | read_sq.rear = 0; | ||
1881 | read_sq.rear_size = 0; | ||
1882 | read_sq.syncing = 0; | ||
1883 | read_sq.active = 0; | ||
1884 | |||
1885 | bdp = rx_base; | ||
1886 | for (i=0; i<numReadBufs; i++) { | ||
1887 | bdp->cbd_bufaddr = virt_to_bus(read_buffers[i]); | ||
1888 | bdp->cbd_datlen = read_sq.block_size; | ||
1889 | bdp++; | ||
1890 | } | ||
1891 | |||
1892 | /* This causes the SMC to sync up with the first buffer again. | ||
1893 | */ | ||
1894 | cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, CPM_CR_INIT_RX) | CPM_CR_FLG; | ||
1895 | while (cp->cp_cpcr & CPM_CR_FLG); | ||
1896 | } | ||
1897 | |||
1898 | |||
1899 | static void sq_play(void) | ||
1900 | { | ||
1901 | (*sound.mach.play)(); | ||
1902 | } | ||
1903 | |||
1904 | |||
1905 | /* ++TeSche: radically changed this one too */ | ||
1906 | |||
1907 | static ssize_t sq_write(struct file *file, const char *src, size_t uLeft, | ||
1908 | loff_t *ppos) | ||
1909 | { | ||
1910 | ssize_t uWritten = 0; | ||
1911 | u_char *dest; | ||
1912 | ssize_t uUsed, bUsed, bLeft; | ||
1913 | |||
1914 | /* ++TeSche: Is something like this necessary? | ||
1915 | * Hey, that's an honest question! Or does any other part of the | ||
1916 | * filesystem already checks this situation? I really don't know. | ||
1917 | */ | ||
1918 | if (uLeft == 0) | ||
1919 | return 0; | ||
1920 | |||
1921 | /* The interrupt doesn't start to play the last, incomplete frame. | ||
1922 | * Thus we can append to it without disabling the interrupts! (Note | ||
1923 | * also that sq.rear isn't affected by the interrupt.) | ||
1924 | */ | ||
1925 | |||
1926 | if (sq.count > 0 && (bLeft = sq.block_size-sq.rear_size) > 0) { | ||
1927 | dest = sq_block_address(sq.rear); | ||
1928 | bUsed = sq.rear_size; | ||
1929 | uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft); | ||
1930 | if (uUsed <= 0) | ||
1931 | return uUsed; | ||
1932 | src += uUsed; | ||
1933 | uWritten += uUsed; | ||
1934 | uLeft -= uUsed; | ||
1935 | sq.rear_size = bUsed; | ||
1936 | } | ||
1937 | |||
1938 | do { | ||
1939 | while (sq.count == sq.max_active) { | ||
1940 | sq_play(); | ||
1941 | if (NON_BLOCKING(sq.open_mode)) | ||
1942 | return uWritten > 0 ? uWritten : -EAGAIN; | ||
1943 | SLEEP(sq.action_queue); | ||
1944 | if (SIGNAL_RECEIVED) | ||
1945 | return uWritten > 0 ? uWritten : -EINTR; | ||
1946 | } | ||
1947 | |||
1948 | /* Here, we can avoid disabling the interrupt by first | ||
1949 | * copying and translating the data, and then updating | ||
1950 | * the sq variables. Until this is done, the interrupt | ||
1951 | * won't see the new frame and we can work on it | ||
1952 | * undisturbed. | ||
1953 | */ | ||
1954 | |||
1955 | dest = sq_block_address((sq.rear+1) % sq.max_count); | ||
1956 | bUsed = 0; | ||
1957 | bLeft = sq.block_size; | ||
1958 | uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft); | ||
1959 | if (uUsed <= 0) | ||
1960 | break; | ||
1961 | src += uUsed; | ||
1962 | uWritten += uUsed; | ||
1963 | uLeft -= uUsed; | ||
1964 | if (bUsed) { | ||
1965 | sq.rear = (sq.rear+1) % sq.max_count; | ||
1966 | sq.rear_size = bUsed; | ||
1967 | sq.count++; | ||
1968 | } | ||
1969 | } while (bUsed); /* uUsed may have been 0 */ | ||
1970 | |||
1971 | sq_play(); | ||
1972 | |||
1973 | return uUsed < 0? uUsed: uWritten; | ||
1974 | } | ||
1975 | |||
1976 | |||
1977 | /***********/ | ||
1978 | |||
1979 | /* Here is how the values are used for reading. | ||
1980 | * The value 'active' simply indicates the DMA is running. This is | ||
1981 | * done so the driver semantics are DMA starts when the first read is | ||
1982 | * posted. The value 'front' indicates the buffer we should next | ||
1983 | * send to the user. The value 'rear' indicates the buffer the DMA is | ||
1984 | * currently filling. When 'front' == 'rear' the buffer "ring" is | ||
1985 | * empty (we always have an empty available). The 'rear_size' is used | ||
1986 | * to track partial offsets into the current buffer. Right now, I just keep | ||
1987 | * The DMA running. If the reader can't keep up, the interrupt tosses | ||
1988 | * the oldest buffer. We could also shut down the DMA in this case. | ||
1989 | */ | ||
1990 | static ssize_t sq_read(struct file *file, char *dst, size_t uLeft, | ||
1991 | loff_t *ppos) | ||
1992 | { | ||
1993 | |||
1994 | ssize_t uRead, bLeft, bUsed, uUsed; | ||
1995 | |||
1996 | if (uLeft == 0) | ||
1997 | return 0; | ||
1998 | |||
1999 | if (!read_sq.active) | ||
2000 | CS_Record(); /* Kick off the record process. */ | ||
2001 | |||
2002 | uRead = 0; | ||
2003 | |||
2004 | /* Move what the user requests, depending upon other options. | ||
2005 | */ | ||
2006 | while (uLeft > 0) { | ||
2007 | |||
2008 | /* When front == rear, the DMA is not done yet. | ||
2009 | */ | ||
2010 | while (read_sq.front == read_sq.rear) { | ||
2011 | if (NON_BLOCKING(read_sq.open_mode)) { | ||
2012 | return uRead > 0 ? uRead : -EAGAIN; | ||
2013 | } | ||
2014 | SLEEP(read_sq.action_queue); | ||
2015 | if (SIGNAL_RECEIVED) | ||
2016 | return uRead > 0 ? uRead : -EINTR; | ||
2017 | } | ||
2018 | |||
2019 | /* The amount we move is either what is left in the | ||
2020 | * current buffer or what the user wants. | ||
2021 | */ | ||
2022 | bLeft = read_sq.block_size - read_sq.rear_size; | ||
2023 | bUsed = read_sq.rear_size; | ||
2024 | uUsed = sound_copy_translate_read(dst, uLeft, | ||
2025 | read_sq.buffers[read_sq.front], &bUsed, bLeft); | ||
2026 | if (uUsed <= 0) | ||
2027 | return uUsed; | ||
2028 | dst += uUsed; | ||
2029 | uRead += uUsed; | ||
2030 | uLeft -= uUsed; | ||
2031 | read_sq.rear_size += bUsed; | ||
2032 | if (read_sq.rear_size >= read_sq.block_size) { | ||
2033 | read_sq.rear_size = 0; | ||
2034 | read_sq.front++; | ||
2035 | if (read_sq.front >= read_sq.max_active) | ||
2036 | read_sq.front = 0; | ||
2037 | } | ||
2038 | } | ||
2039 | return uRead; | ||
2040 | } | ||
2041 | |||
2042 | static int sq_open(struct inode *inode, struct file *file) | ||
2043 | { | ||
2044 | int rc = 0; | ||
2045 | |||
2046 | if (file->f_mode & FMODE_WRITE) { | ||
2047 | if (sq.busy) { | ||
2048 | rc = -EBUSY; | ||
2049 | if (NON_BLOCKING(file->f_flags)) | ||
2050 | goto err_out; | ||
2051 | rc = -EINTR; | ||
2052 | while (sq.busy) { | ||
2053 | SLEEP(sq.open_queue); | ||
2054 | if (SIGNAL_RECEIVED) | ||
2055 | goto err_out; | ||
2056 | } | ||
2057 | } | ||
2058 | sq.busy = 1; /* Let's play spot-the-race-condition */ | ||
2059 | |||
2060 | if (sq_allocate_buffers()) goto err_out_nobusy; | ||
2061 | |||
2062 | sq_setup(numBufs, bufSize<<10,sound_buffers); | ||
2063 | sq.open_mode = file->f_mode; | ||
2064 | } | ||
2065 | |||
2066 | |||
2067 | if (file->f_mode & FMODE_READ) { | ||
2068 | if (read_sq.busy) { | ||
2069 | rc = -EBUSY; | ||
2070 | if (NON_BLOCKING(file->f_flags)) | ||
2071 | goto err_out; | ||
2072 | rc = -EINTR; | ||
2073 | while (read_sq.busy) { | ||
2074 | SLEEP(read_sq.open_queue); | ||
2075 | if (SIGNAL_RECEIVED) | ||
2076 | goto err_out; | ||
2077 | } | ||
2078 | rc = 0; | ||
2079 | } | ||
2080 | read_sq.busy = 1; | ||
2081 | if (sq_allocate_read_buffers()) goto err_out_nobusy; | ||
2082 | |||
2083 | read_sq_setup(numReadBufs,readbufSize<<10, sound_read_buffers); | ||
2084 | read_sq.open_mode = file->f_mode; | ||
2085 | } | ||
2086 | |||
2087 | /* Start up the 4218 by: | ||
2088 | * Reset. | ||
2089 | * Enable, unreset. | ||
2090 | */ | ||
2091 | *((volatile uint *)HIOX_CSR4_ADDR) &= ~HIOX_CSR4_RSTAUDIO; | ||
2092 | eieio(); | ||
2093 | *((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_ENAUDIO; | ||
2094 | mdelay(50); | ||
2095 | *((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_RSTAUDIO; | ||
2096 | |||
2097 | /* We need to send the current control word in case someone | ||
2098 | * opened /dev/mixer and changed things while we were shut | ||
2099 | * down. Chances are good the initialization that follows | ||
2100 | * would have done this, but it is still possible it wouldn't. | ||
2101 | */ | ||
2102 | cs4218_ctl_write(cs4218_control); | ||
2103 | |||
2104 | sound.minDev = iminor(inode) & 0x0f; | ||
2105 | sound.soft = sound.dsp; | ||
2106 | sound.hard = sound.dsp; | ||
2107 | sound_init(); | ||
2108 | if ((iminor(inode) & 0x0f) == SND_DEV_AUDIO) { | ||
2109 | sound_set_speed(8000); | ||
2110 | sound_set_stereo(0); | ||
2111 | sound_set_format(AFMT_MU_LAW); | ||
2112 | } | ||
2113 | |||
2114 | return nonseekable_open(inode, file); | ||
2115 | |||
2116 | err_out_nobusy: | ||
2117 | if (file->f_mode & FMODE_WRITE) { | ||
2118 | sq.busy = 0; | ||
2119 | WAKE_UP(sq.open_queue); | ||
2120 | } | ||
2121 | if (file->f_mode & FMODE_READ) { | ||
2122 | read_sq.busy = 0; | ||
2123 | WAKE_UP(read_sq.open_queue); | ||
2124 | } | ||
2125 | err_out: | ||
2126 | return rc; | ||
2127 | } | ||
2128 | |||
2129 | |||
2130 | static void sq_reset(void) | ||
2131 | { | ||
2132 | sound_silence(); | ||
2133 | sq.active = 0; | ||
2134 | sq.count = 0; | ||
2135 | sq.front = (sq.rear+1) % sq.max_count; | ||
2136 | #if 0 | ||
2137 | init_tdm_buffers(); | ||
2138 | #endif | ||
2139 | } | ||
2140 | |||
2141 | |||
2142 | static int sq_fsync(struct file *filp, struct dentry *dentry) | ||
2143 | { | ||
2144 | int rc = 0; | ||
2145 | |||
2146 | sq.syncing = 1; | ||
2147 | sq_play(); /* there may be an incomplete frame waiting */ | ||
2148 | |||
2149 | while (sq.active) { | ||
2150 | SLEEP(sq.sync_queue); | ||
2151 | if (SIGNAL_RECEIVED) { | ||
2152 | /* While waiting for audio output to drain, an | ||
2153 | * interrupt occurred. Stop audio output immediately | ||
2154 | * and clear the queue. */ | ||
2155 | sq_reset(); | ||
2156 | rc = -EINTR; | ||
2157 | break; | ||
2158 | } | ||
2159 | } | ||
2160 | |||
2161 | sq.syncing = 0; | ||
2162 | return rc; | ||
2163 | } | ||
2164 | |||
2165 | static int sq_release(struct inode *inode, struct file *file) | ||
2166 | { | ||
2167 | int rc = 0; | ||
2168 | |||
2169 | if (sq.busy) | ||
2170 | rc = sq_fsync(file, file->f_dentry); | ||
2171 | sound.soft = sound.dsp; | ||
2172 | sound.hard = sound.dsp; | ||
2173 | sound_silence(); | ||
2174 | |||
2175 | sq_release_read_buffers(); | ||
2176 | sq_release_buffers(); | ||
2177 | |||
2178 | if (file->f_mode & FMODE_READ) { | ||
2179 | read_sq.busy = 0; | ||
2180 | WAKE_UP(read_sq.open_queue); | ||
2181 | } | ||
2182 | |||
2183 | if (file->f_mode & FMODE_WRITE) { | ||
2184 | sq.busy = 0; | ||
2185 | WAKE_UP(sq.open_queue); | ||
2186 | } | ||
2187 | |||
2188 | /* Shut down the SMC. | ||
2189 | */ | ||
2190 | cpmp->cp_smc[1].smc_smcmr &= ~(SMCMR_TEN | SMCMR_REN); | ||
2191 | |||
2192 | /* Shut down the codec. | ||
2193 | */ | ||
2194 | *((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_RSTAUDIO; | ||
2195 | eieio(); | ||
2196 | *((volatile uint *)HIOX_CSR4_ADDR) &= ~HIOX_CSR4_ENAUDIO; | ||
2197 | |||
2198 | /* Wake up a process waiting for the queue being released. | ||
2199 | * Note: There may be several processes waiting for a call | ||
2200 | * to open() returning. */ | ||
2201 | |||
2202 | return rc; | ||
2203 | } | ||
2204 | |||
2205 | |||
2206 | static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd, | ||
2207 | u_long arg) | ||
2208 | { | ||
2209 | u_long fmt; | ||
2210 | int data; | ||
2211 | #if 0 | ||
2212 | int size, nbufs; | ||
2213 | #else | ||
2214 | int size; | ||
2215 | #endif | ||
2216 | |||
2217 | switch (cmd) { | ||
2218 | case SNDCTL_DSP_RESET: | ||
2219 | sq_reset(); | ||
2220 | return 0; | ||
2221 | case SNDCTL_DSP_POST: | ||
2222 | case SNDCTL_DSP_SYNC: | ||
2223 | return sq_fsync(file, file->f_dentry); | ||
2224 | |||
2225 | /* ++TeSche: before changing any of these it's | ||
2226 | * probably wise to wait until sound playing has | ||
2227 | * settled down. */ | ||
2228 | case SNDCTL_DSP_SPEED: | ||
2229 | sq_fsync(file, file->f_dentry); | ||
2230 | IOCTL_IN(arg, data); | ||
2231 | return IOCTL_OUT(arg, sound_set_speed(data)); | ||
2232 | case SNDCTL_DSP_STEREO: | ||
2233 | sq_fsync(file, file->f_dentry); | ||
2234 | IOCTL_IN(arg, data); | ||
2235 | return IOCTL_OUT(arg, sound_set_stereo(data)); | ||
2236 | case SOUND_PCM_WRITE_CHANNELS: | ||
2237 | sq_fsync(file, file->f_dentry); | ||
2238 | IOCTL_IN(arg, data); | ||
2239 | return IOCTL_OUT(arg, sound_set_stereo(data-1)+1); | ||
2240 | case SNDCTL_DSP_SETFMT: | ||
2241 | sq_fsync(file, file->f_dentry); | ||
2242 | IOCTL_IN(arg, data); | ||
2243 | return IOCTL_OUT(arg, sound_set_format(data)); | ||
2244 | case SNDCTL_DSP_GETFMTS: | ||
2245 | fmt = 0; | ||
2246 | if (sound.trans_write) { | ||
2247 | if (sound.trans_write->ct_ulaw) | ||
2248 | fmt |= AFMT_MU_LAW; | ||
2249 | if (sound.trans_write->ct_alaw) | ||
2250 | fmt |= AFMT_A_LAW; | ||
2251 | if (sound.trans_write->ct_s8) | ||
2252 | fmt |= AFMT_S8; | ||
2253 | if (sound.trans_write->ct_u8) | ||
2254 | fmt |= AFMT_U8; | ||
2255 | if (sound.trans_write->ct_s16be) | ||
2256 | fmt |= AFMT_S16_BE; | ||
2257 | if (sound.trans_write->ct_u16be) | ||
2258 | fmt |= AFMT_U16_BE; | ||
2259 | if (sound.trans_write->ct_s16le) | ||
2260 | fmt |= AFMT_S16_LE; | ||
2261 | if (sound.trans_write->ct_u16le) | ||
2262 | fmt |= AFMT_U16_LE; | ||
2263 | } | ||
2264 | return IOCTL_OUT(arg, fmt); | ||
2265 | case SNDCTL_DSP_GETBLKSIZE: | ||
2266 | size = sq.block_size | ||
2267 | * sound.soft.size * (sound.soft.stereo + 1) | ||
2268 | / (sound.hard.size * (sound.hard.stereo + 1)); | ||
2269 | return IOCTL_OUT(arg, size); | ||
2270 | case SNDCTL_DSP_SUBDIVIDE: | ||
2271 | break; | ||
2272 | #if 0 /* Sorry can't do this at the moment. The CPM allocated buffers | ||
2273 | * long ago that can't be changed. | ||
2274 | */ | ||
2275 | case SNDCTL_DSP_SETFRAGMENT: | ||
2276 | if (sq.count || sq.active || sq.syncing) | ||
2277 | return -EINVAL; | ||
2278 | IOCTL_IN(arg, size); | ||
2279 | nbufs = size >> 16; | ||
2280 | if (nbufs < 2 || nbufs > numBufs) | ||
2281 | nbufs = numBufs; | ||
2282 | size &= 0xffff; | ||
2283 | if (size >= 8 && size <= 30) { | ||
2284 | size = 1 << size; | ||
2285 | size *= sound.hard.size * (sound.hard.stereo + 1); | ||
2286 | size /= sound.soft.size * (sound.soft.stereo + 1); | ||
2287 | if (size > (bufSize << 10)) | ||
2288 | size = bufSize << 10; | ||
2289 | } else | ||
2290 | size = bufSize << 10; | ||
2291 | sq_setup(numBufs, size, sound_buffers); | ||
2292 | sq.max_active = nbufs; | ||
2293 | return 0; | ||
2294 | #endif | ||
2295 | |||
2296 | default: | ||
2297 | return mixer_ioctl(inode, file, cmd, arg); | ||
2298 | } | ||
2299 | return -EINVAL; | ||
2300 | } | ||
2301 | |||
2302 | |||
2303 | |||
2304 | static struct file_operations sq_fops = | ||
2305 | { | ||
2306 | .owner = THIS_MODULE, | ||
2307 | .llseek = sound_lseek, | ||
2308 | .read = sq_read, /* sq_read */ | ||
2309 | .write = sq_write, | ||
2310 | .ioctl = sq_ioctl, | ||
2311 | .open = sq_open, | ||
2312 | .release = sq_release, | ||
2313 | }; | ||
2314 | |||
2315 | |||
2316 | static void __init sq_init(void) | ||
2317 | { | ||
2318 | sq_unit = register_sound_dsp(&sq_fops, -1); | ||
2319 | if (sq_unit < 0) | ||
2320 | return; | ||
2321 | |||
2322 | init_waitqueue_head(&sq.action_queue); | ||
2323 | init_waitqueue_head(&sq.open_queue); | ||
2324 | init_waitqueue_head(&sq.sync_queue); | ||
2325 | init_waitqueue_head(&read_sq.action_queue); | ||
2326 | init_waitqueue_head(&read_sq.open_queue); | ||
2327 | init_waitqueue_head(&read_sq.sync_queue); | ||
2328 | |||
2329 | sq.busy = 0; | ||
2330 | read_sq.busy = 0; | ||
2331 | |||
2332 | /* whatever you like as startup mode for /dev/dsp, | ||
2333 | * (/dev/audio hasn't got a startup mode). note that | ||
2334 | * once changed a new open() will *not* restore these! | ||
2335 | */ | ||
2336 | sound.dsp.format = AFMT_S16_BE; | ||
2337 | sound.dsp.stereo = 1; | ||
2338 | sound.dsp.size = 16; | ||
2339 | |||
2340 | /* set minimum rate possible without expanding */ | ||
2341 | sound.dsp.speed = 8000; | ||
2342 | |||
2343 | /* before the first open to /dev/dsp this wouldn't be set */ | ||
2344 | sound.soft = sound.dsp; | ||
2345 | sound.hard = sound.dsp; | ||
2346 | |||
2347 | sound_silence(); | ||
2348 | } | ||
2349 | |||
2350 | /* | ||
2351 | * /dev/sndstat | ||
2352 | */ | ||
2353 | |||
2354 | |||
2355 | /* state.buf should not overflow! */ | ||
2356 | |||
2357 | static int state_open(struct inode *inode, struct file *file) | ||
2358 | { | ||
2359 | char *buffer = state.buf, *mach = "", cs4218_buf[50]; | ||
2360 | int len = 0; | ||
2361 | |||
2362 | if (state.busy) | ||
2363 | return -EBUSY; | ||
2364 | |||
2365 | state.ptr = 0; | ||
2366 | state.busy = 1; | ||
2367 | |||
2368 | sprintf(cs4218_buf, "Crystal CS4218 on TDM, "); | ||
2369 | mach = cs4218_buf; | ||
2370 | |||
2371 | len += sprintf(buffer+len, "%sDMA sound driver:\n", mach); | ||
2372 | |||
2373 | len += sprintf(buffer+len, "\tsound.format = 0x%x", sound.soft.format); | ||
2374 | switch (sound.soft.format) { | ||
2375 | case AFMT_MU_LAW: | ||
2376 | len += sprintf(buffer+len, " (mu-law)"); | ||
2377 | break; | ||
2378 | case AFMT_A_LAW: | ||
2379 | len += sprintf(buffer+len, " (A-law)"); | ||
2380 | break; | ||
2381 | case AFMT_U8: | ||
2382 | len += sprintf(buffer+len, " (unsigned 8 bit)"); | ||
2383 | break; | ||
2384 | case AFMT_S8: | ||
2385 | len += sprintf(buffer+len, " (signed 8 bit)"); | ||
2386 | break; | ||
2387 | case AFMT_S16_BE: | ||
2388 | len += sprintf(buffer+len, " (signed 16 bit big)"); | ||
2389 | break; | ||
2390 | case AFMT_U16_BE: | ||
2391 | len += sprintf(buffer+len, " (unsigned 16 bit big)"); | ||
2392 | break; | ||
2393 | case AFMT_S16_LE: | ||
2394 | len += sprintf(buffer+len, " (signed 16 bit little)"); | ||
2395 | break; | ||
2396 | case AFMT_U16_LE: | ||
2397 | len += sprintf(buffer+len, " (unsigned 16 bit little)"); | ||
2398 | break; | ||
2399 | } | ||
2400 | len += sprintf(buffer+len, "\n"); | ||
2401 | len += sprintf(buffer+len, "\tsound.speed = %dHz (phys. %dHz)\n", | ||
2402 | sound.soft.speed, sound.hard.speed); | ||
2403 | len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n", | ||
2404 | sound.soft.stereo, sound.soft.stereo ? "stereo" : "mono"); | ||
2405 | len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d" | ||
2406 | " sq.max_active = %d\n", | ||
2407 | sq.block_size, sq.max_count, sq.max_active); | ||
2408 | len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n", sq.count, | ||
2409 | sq.rear_size); | ||
2410 | len += sprintf(buffer+len, "\tsq.active = %d sq.syncing = %d\n", | ||
2411 | sq.active, sq.syncing); | ||
2412 | state.len = len; | ||
2413 | return nonseekable_open(inode, file); | ||
2414 | } | ||
2415 | |||
2416 | |||
2417 | static int state_release(struct inode *inode, struct file *file) | ||
2418 | { | ||
2419 | state.busy = 0; | ||
2420 | return 0; | ||
2421 | } | ||
2422 | |||
2423 | |||
2424 | static ssize_t state_read(struct file *file, char *buf, size_t count, | ||
2425 | loff_t *ppos) | ||
2426 | { | ||
2427 | int n = state.len - state.ptr; | ||
2428 | if (n > count) | ||
2429 | n = count; | ||
2430 | if (n <= 0) | ||
2431 | return 0; | ||
2432 | if (copy_to_user(buf, &state.buf[state.ptr], n)) | ||
2433 | return -EFAULT; | ||
2434 | state.ptr += n; | ||
2435 | return n; | ||
2436 | } | ||
2437 | |||
2438 | |||
2439 | static struct file_operations state_fops = | ||
2440 | { | ||
2441 | .owner = THIS_MODULE, | ||
2442 | .llseek = sound_lseek, | ||
2443 | .read = state_read, | ||
2444 | .open = state_open, | ||
2445 | .release = state_release, | ||
2446 | }; | ||
2447 | |||
2448 | |||
2449 | static void __init state_init(void) | ||
2450 | { | ||
2451 | state_unit = register_sound_special(&state_fops, SND_DEV_STATUS); | ||
2452 | if (state_unit < 0) | ||
2453 | return; | ||
2454 | state.busy = 0; | ||
2455 | } | ||
2456 | |||
2457 | |||
2458 | /*** Common stuff ********************************************************/ | ||
2459 | |||
2460 | static long long sound_lseek(struct file *file, long long offset, int orig) | ||
2461 | { | ||
2462 | return -ESPIPE; | ||
2463 | } | ||
2464 | |||
2465 | |||
2466 | /*** Config & Setup **********************************************************/ | ||
2467 | |||
2468 | |||
2469 | int __init tdm8xx_sound_init(void) | ||
2470 | { | ||
2471 | int i, has_sound; | ||
2472 | uint dp_offset; | ||
2473 | volatile uint *sirp; | ||
2474 | volatile cbd_t *bdp; | ||
2475 | volatile cpm8xx_t *cp; | ||
2476 | volatile smc_t *sp; | ||
2477 | volatile smc_uart_t *up; | ||
2478 | volatile immap_t *immap; | ||
2479 | |||
2480 | has_sound = 0; | ||
2481 | |||
2482 | /* Program the SI/TSA to use TDMa, connected to SMC2, for 4 bytes. | ||
2483 | */ | ||
2484 | cp = cpmp; /* Get pointer to Communication Processor */ | ||
2485 | immap = (immap_t *)IMAP_ADDR; /* and to internal registers */ | ||
2486 | |||
2487 | /* Set all TDMa control bits to zero. This enables most features | ||
2488 | * we want. | ||
2489 | */ | ||
2490 | cp->cp_simode &= ~0x00000fff; | ||
2491 | |||
2492 | /* Enable common receive/transmit clock pins, use IDL format. | ||
2493 | * Sync on falling edge, transmit rising clock, receive falling | ||
2494 | * clock, delay 1 bit on both Tx and Rx. Common Tx/Rx clocks and | ||
2495 | * sync. | ||
2496 | * Connect SMC2 to TSA. | ||
2497 | */ | ||
2498 | cp->cp_simode |= 0x80000141; | ||
2499 | |||
2500 | /* Configure port A pins for TDMa operation. | ||
2501 | * The RPX-Lite (MPC850/823) loses SMC2 when TDM is used. | ||
2502 | */ | ||
2503 | immap->im_ioport.iop_papar |= 0x01c0; /* Enable TDMa functions */ | ||
2504 | immap->im_ioport.iop_padir |= 0x00c0; /* Enable TDMa Tx/Rx */ | ||
2505 | immap->im_ioport.iop_padir &= ~0x0100; /* Enable L1RCLKa */ | ||
2506 | |||
2507 | immap->im_ioport.iop_pcpar |= 0x0800; /* Enable L1RSYNCa */ | ||
2508 | immap->im_ioport.iop_pcdir &= ~0x0800; | ||
2509 | |||
2510 | /* Initialize the SI TDM routing table. We use TDMa only. | ||
2511 | * The receive table and transmit table each have only one | ||
2512 | * entry, to capture/send four bytes after each frame pulse. | ||
2513 | * The 16-bit ram entry is 0000 0001 1000 1111. (SMC2) | ||
2514 | */ | ||
2515 | cp->cp_sigmr = 0; | ||
2516 | sirp = (uint *)cp->cp_siram; | ||
2517 | |||
2518 | *sirp = 0x018f0000; /* Receive entry */ | ||
2519 | sirp += 64; | ||
2520 | *sirp = 0x018f0000; /* Tramsmit entry */ | ||
2521 | |||
2522 | /* Enable single TDMa routing. | ||
2523 | */ | ||
2524 | cp->cp_sigmr = 0x04; | ||
2525 | |||
2526 | /* Initialize the SMC for transparent operation. | ||
2527 | */ | ||
2528 | sp = &cpmp->cp_smc[1]; | ||
2529 | up = (smc_uart_t *)&cp->cp_dparam[PROFF_SMC2]; | ||
2530 | |||
2531 | /* We need to allocate a transmit and receive buffer | ||
2532 | * descriptors from dual port ram. | ||
2533 | */ | ||
2534 | dp_addr = cpm_dpalloc(sizeof(cbd_t) * numReadBufs, 8); | ||
2535 | |||
2536 | /* Set the physical address of the host memory | ||
2537 | * buffers in the buffer descriptors, and the | ||
2538 | * virtual address for us to work with. | ||
2539 | */ | ||
2540 | bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; | ||
2541 | up->smc_rbase = dp_offset; | ||
2542 | rx_cur = rx_base = (cbd_t *)bdp; | ||
2543 | |||
2544 | for (i=0; i<(numReadBufs-1); i++) { | ||
2545 | bdp->cbd_bufaddr = 0; | ||
2546 | bdp->cbd_datlen = 0; | ||
2547 | bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT; | ||
2548 | bdp++; | ||
2549 | } | ||
2550 | bdp->cbd_bufaddr = 0; | ||
2551 | bdp->cbd_datlen = 0; | ||
2552 | bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT; | ||
2553 | |||
2554 | /* Now, do the same for the transmit buffers. | ||
2555 | */ | ||
2556 | dp_offset = cpm_dpalloc(sizeof(cbd_t) * numBufs, 8); | ||
2557 | |||
2558 | bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; | ||
2559 | up->smc_tbase = dp_offset; | ||
2560 | tx_cur = tx_base = (cbd_t *)bdp; | ||
2561 | |||
2562 | for (i=0; i<(numBufs-1); i++) { | ||
2563 | bdp->cbd_bufaddr = 0; | ||
2564 | bdp->cbd_datlen = 0; | ||
2565 | bdp->cbd_sc = BD_SC_INTRPT; | ||
2566 | bdp++; | ||
2567 | } | ||
2568 | bdp->cbd_bufaddr = 0; | ||
2569 | bdp->cbd_datlen = 0; | ||
2570 | bdp->cbd_sc = (BD_SC_WRAP | BD_SC_INTRPT); | ||
2571 | |||
2572 | /* Set transparent SMC mode. | ||
2573 | * A few things are specific to our application. The codec interface | ||
2574 | * is MSB first, hence the REVD selection. The CD/CTS pulse are | ||
2575 | * used by the TSA to indicate the frame start to the SMC. | ||
2576 | */ | ||
2577 | up->smc_rfcr = SCC_EB; | ||
2578 | up->smc_tfcr = SCC_EB; | ||
2579 | up->smc_mrblr = readbufSize * 1024; | ||
2580 | |||
2581 | /* Set 16-bit reversed data, transparent mode. | ||
2582 | */ | ||
2583 | sp->smc_smcmr = smcr_mk_clen(15) | | ||
2584 | SMCMR_SM_TRANS | SMCMR_REVD | SMCMR_BS; | ||
2585 | |||
2586 | /* Enable and clear events. | ||
2587 | * Because of FIFO delays, all we need is the receive interrupt | ||
2588 | * and we can process both the current receive and current | ||
2589 | * transmit interrupt within a few microseconds of the transmit. | ||
2590 | */ | ||
2591 | sp->smc_smce = 0xff; | ||
2592 | sp->smc_smcm = SMCM_TXE | SMCM_TX | SMCM_RX; | ||
2593 | |||
2594 | /* Send the CPM an initialize command. | ||
2595 | */ | ||
2596 | cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, | ||
2597 | CPM_CR_INIT_TRX) | CPM_CR_FLG; | ||
2598 | while (cp->cp_cpcr & CPM_CR_FLG); | ||
2599 | |||
2600 | sound.mach = mach_cs4218; | ||
2601 | has_sound = 1; | ||
2602 | |||
2603 | /* Initialize beep stuff */ | ||
2604 | orig_mksound = kd_mksound; | ||
2605 | kd_mksound = cs_mksound; | ||
2606 | beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL); | ||
2607 | if (beep_buf == NULL) | ||
2608 | printk(KERN_WARNING "dmasound: no memory for " | ||
2609 | "beep buffer\n"); | ||
2610 | |||
2611 | if (!has_sound) | ||
2612 | return -ENODEV; | ||
2613 | |||
2614 | /* Initialize the software SPI. | ||
2615 | */ | ||
2616 | sw_spi_init(); | ||
2617 | |||
2618 | /* Set up sound queue, /dev/audio and /dev/dsp. */ | ||
2619 | |||
2620 | /* Set default settings. */ | ||
2621 | sq_init(); | ||
2622 | |||
2623 | /* Set up /dev/sndstat. */ | ||
2624 | state_init(); | ||
2625 | |||
2626 | /* Set up /dev/mixer. */ | ||
2627 | mixer_init(); | ||
2628 | |||
2629 | if (!sound.mach.irqinit()) { | ||
2630 | printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n"); | ||
2631 | return -ENODEV; | ||
2632 | } | ||
2633 | #ifdef MODULE | ||
2634 | irq_installed = 1; | ||
2635 | #endif | ||
2636 | |||
2637 | printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n", | ||
2638 | numBufs, bufSize); | ||
2639 | |||
2640 | return 0; | ||
2641 | } | ||
2642 | |||
2643 | /* Due to FIFOs and bit delays, the transmit interrupt occurs a few | ||
2644 | * microseconds ahead of the receive interrupt. | ||
2645 | * When we get an interrupt, we service the transmit first, then | ||
2646 | * check for a receive to prevent the overhead of returning through | ||
2647 | * the interrupt handler only to get back here right away during | ||
2648 | * full duplex operation. | ||
2649 | */ | ||
2650 | static void | ||
2651 | cs4218_intr(void *dev_id, struct pt_regs *regs) | ||
2652 | { | ||
2653 | volatile smc_t *sp; | ||
2654 | volatile cpm8xx_t *cp; | ||
2655 | |||
2656 | sp = &cpmp->cp_smc[1]; | ||
2657 | |||
2658 | if (sp->smc_smce & SCCM_TX) { | ||
2659 | sp->smc_smce = SCCM_TX; | ||
2660 | cs4218_tdm_tx_intr((void *)sp); | ||
2661 | } | ||
2662 | |||
2663 | if (sp->smc_smce & SCCM_RX) { | ||
2664 | sp->smc_smce = SCCM_RX; | ||
2665 | cs4218_tdm_rx_intr((void *)sp); | ||
2666 | } | ||
2667 | |||
2668 | if (sp->smc_smce & SCCM_TXE) { | ||
2669 | /* Transmit underrun. This happens with the application | ||
2670 | * didn't keep up sending buffers. We tell the SMC to | ||
2671 | * restart, which will cause it to poll the current (next) | ||
2672 | * BD. If the user supplied data since this occurred, | ||
2673 | * we just start running again. If they didn't, the SMC | ||
2674 | * will poll the descriptor until data is placed there. | ||
2675 | */ | ||
2676 | sp->smc_smce = SCCM_TXE; | ||
2677 | cp = cpmp; /* Get pointer to Communication Processor */ | ||
2678 | cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, | ||
2679 | CPM_CR_RESTART_TX) | CPM_CR_FLG; | ||
2680 | while (cp->cp_cpcr & CPM_CR_FLG); | ||
2681 | } | ||
2682 | } | ||
2683 | |||
2684 | |||
2685 | #define MAXARGS 8 /* Should be sufficient for now */ | ||
2686 | |||
2687 | void __init dmasound_setup(char *str, int *ints) | ||
2688 | { | ||
2689 | /* check the bootstrap parameter for "dmasound=" */ | ||
2690 | |||
2691 | switch (ints[0]) { | ||
2692 | case 3: | ||
2693 | if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS)) | ||
2694 | printk("dmasound_setup: invalid catch radius, using default = %d\n", catchRadius); | ||
2695 | else | ||
2696 | catchRadius = ints[3]; | ||
2697 | /* fall through */ | ||
2698 | case 2: | ||
2699 | if (ints[1] < MIN_BUFFERS) | ||
2700 | printk("dmasound_setup: invalid number of buffers, using default = %d\n", numBufs); | ||
2701 | else | ||
2702 | numBufs = ints[1]; | ||
2703 | if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE) | ||
2704 | printk("dmasound_setup: invalid buffer size, using default = %d\n", bufSize); | ||
2705 | else | ||
2706 | bufSize = ints[2]; | ||
2707 | break; | ||
2708 | case 0: | ||
2709 | break; | ||
2710 | default: | ||
2711 | printk("dmasound_setup: invalid number of arguments\n"); | ||
2712 | } | ||
2713 | } | ||
2714 | |||
2715 | /* Software SPI functions. | ||
2716 | * These are on Port B. | ||
2717 | */ | ||
2718 | #define PB_SPICLK ((uint)0x00000002) | ||
2719 | #define PB_SPIMOSI ((uint)0x00000004) | ||
2720 | #define PB_SPIMISO ((uint)0x00000008) | ||
2721 | |||
2722 | static | ||
2723 | void sw_spi_init(void) | ||
2724 | { | ||
2725 | volatile cpm8xx_t *cp; | ||
2726 | volatile uint *hcsr4; | ||
2727 | |||
2728 | hcsr4 = (volatile uint *)HIOX_CSR4_ADDR; | ||
2729 | cp = cpmp; /* Get pointer to Communication Processor */ | ||
2730 | |||
2731 | *hcsr4 &= ~HIOX_CSR4_AUDSPISEL; /* Disable SPI select */ | ||
2732 | |||
2733 | /* Make these Port B signals general purpose I/O. | ||
2734 | * First, make sure the clock is low. | ||
2735 | */ | ||
2736 | cp->cp_pbdat &= ~PB_SPICLK; | ||
2737 | cp->cp_pbpar &= ~(PB_SPICLK | PB_SPIMOSI | PB_SPIMISO); | ||
2738 | |||
2739 | /* Clock and Master Output are outputs. | ||
2740 | */ | ||
2741 | cp->cp_pbdir |= (PB_SPICLK | PB_SPIMOSI); | ||
2742 | |||
2743 | /* Master Input. | ||
2744 | */ | ||
2745 | cp->cp_pbdir &= ~PB_SPIMISO; | ||
2746 | |||
2747 | } | ||
2748 | |||
2749 | /* Write the CS4218 control word out the SPI port. While the | ||
2750 | * the control word is going out, the status word is arriving. | ||
2751 | */ | ||
2752 | static | ||
2753 | uint cs4218_ctl_write(uint ctlreg) | ||
2754 | { | ||
2755 | uint status; | ||
2756 | |||
2757 | sw_spi_io((u_char *)&ctlreg, (u_char *)&status, 4); | ||
2758 | |||
2759 | /* Shadow the control register.....I guess we could do | ||
2760 | * the same for the status, but for now we just return it | ||
2761 | * and let the caller decide. | ||
2762 | */ | ||
2763 | cs4218_control = ctlreg; | ||
2764 | return status; | ||
2765 | } | ||
2766 | |||
2767 | static | ||
2768 | void sw_spi_io(u_char *obuf, u_char *ibuf, uint bcnt) | ||
2769 | { | ||
2770 | int bits, i; | ||
2771 | u_char outbyte, inbyte; | ||
2772 | volatile cpm8xx_t *cp; | ||
2773 | volatile uint *hcsr4; | ||
2774 | |||
2775 | hcsr4 = (volatile uint *)HIOX_CSR4_ADDR; | ||
2776 | cp = cpmp; /* Get pointer to Communication Processor */ | ||
2777 | |||
2778 | /* The timing on the bus is pretty slow. Code inefficiency | ||
2779 | * and eieio() is our friend here :-). | ||
2780 | */ | ||
2781 | cp->cp_pbdat &= ~PB_SPICLK; | ||
2782 | *hcsr4 |= HIOX_CSR4_AUDSPISEL; /* Enable SPI select */ | ||
2783 | eieio(); | ||
2784 | |||
2785 | /* Clock in/out the bytes. Data is valid on the falling edge | ||
2786 | * of the clock. Data is MSB first. | ||
2787 | */ | ||
2788 | for (i=0; i<bcnt; i++) { | ||
2789 | outbyte = *obuf++; | ||
2790 | inbyte = 0; | ||
2791 | for (bits=0; bits<8; bits++) { | ||
2792 | eieio(); | ||
2793 | cp->cp_pbdat |= PB_SPICLK; | ||
2794 | eieio(); | ||
2795 | if (outbyte & 0x80) | ||
2796 | cp->cp_pbdat |= PB_SPIMOSI; | ||
2797 | else | ||
2798 | cp->cp_pbdat &= ~PB_SPIMOSI; | ||
2799 | eieio(); | ||
2800 | cp->cp_pbdat &= ~PB_SPICLK; | ||
2801 | eieio(); | ||
2802 | outbyte <<= 1; | ||
2803 | inbyte <<= 1; | ||
2804 | if (cp->cp_pbdat & PB_SPIMISO) | ||
2805 | inbyte |= 1; | ||
2806 | } | ||
2807 | *ibuf++ = inbyte; | ||
2808 | } | ||
2809 | |||
2810 | *hcsr4 &= ~HIOX_CSR4_AUDSPISEL; /* Disable SPI select */ | ||
2811 | eieio(); | ||
2812 | } | ||
2813 | |||
2814 | void cleanup_module(void) | ||
2815 | { | ||
2816 | if (irq_installed) { | ||
2817 | sound_silence(); | ||
2818 | #ifdef MODULE | ||
2819 | sound.mach.irqcleanup(); | ||
2820 | #endif | ||
2821 | } | ||
2822 | |||
2823 | sq_release_read_buffers(); | ||
2824 | sq_release_buffers(); | ||
2825 | |||
2826 | if (mixer_unit >= 0) | ||
2827 | unregister_sound_mixer(mixer_unit); | ||
2828 | if (state_unit >= 0) | ||
2829 | unregister_sound_special(state_unit); | ||
2830 | if (sq_unit >= 0) | ||
2831 | unregister_sound_dsp(sq_unit); | ||
2832 | } | ||
2833 | |||
2834 | module_init(tdm8xx_sound_init); | ||
2835 | module_exit(cleanup_module); | ||
2836 | |||
diff --git a/arch/ppc/8xx_io/enet.c b/arch/ppc/8xx_io/enet.c new file mode 100644 index 000000000000..4ea7158e5062 --- /dev/null +++ b/arch/ppc/8xx_io/enet.c | |||
@@ -0,0 +1,971 @@ | |||
1 | /* | ||
2 | * Ethernet driver for Motorola MPC8xx. | ||
3 | * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) | ||
4 | * | ||
5 | * I copied the basic skeleton from the lance driver, because I did not | ||
6 | * know how to write the Linux driver, but I did know how the LANCE worked. | ||
7 | * | ||
8 | * This version of the driver is somewhat selectable for the different | ||
9 | * processor/board combinations. It works for the boards I know about | ||
10 | * now, and should be easily modified to include others. Some of the | ||
11 | * configuration information is contained in <asm/commproc.h> and the | ||
12 | * remainder is here. | ||
13 | * | ||
14 | * Buffer descriptors are kept in the CPM dual port RAM, and the frame | ||
15 | * buffers are in the host memory. | ||
16 | * | ||
17 | * Right now, I am very watseful with the buffers. I allocate memory | ||
18 | * pages and then divide them into 2K frame buffers. This way I know I | ||
19 | * have buffers large enough to hold one frame within one buffer descriptor. | ||
20 | * Once I get this working, I will use 64 or 128 byte CPM buffers, which | ||
21 | * will be much more memory efficient and will easily handle lots of | ||
22 | * small packets. | ||
23 | * | ||
24 | */ | ||
25 | #include <linux/config.h> | ||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/sched.h> | ||
28 | #include <linux/string.h> | ||
29 | #include <linux/ptrace.h> | ||
30 | #include <linux/errno.h> | ||
31 | #include <linux/ioport.h> | ||
32 | #include <linux/slab.h> | ||
33 | #include <linux/interrupt.h> | ||
34 | #include <linux/pci.h> | ||
35 | #include <linux/init.h> | ||
36 | #include <linux/delay.h> | ||
37 | #include <linux/netdevice.h> | ||
38 | #include <linux/etherdevice.h> | ||
39 | #include <linux/skbuff.h> | ||
40 | #include <linux/spinlock.h> | ||
41 | #include <linux/dma-mapping.h> | ||
42 | #include <linux/bitops.h> | ||
43 | |||
44 | #include <asm/8xx_immap.h> | ||
45 | #include <asm/pgtable.h> | ||
46 | #include <asm/mpc8xx.h> | ||
47 | #include <asm/uaccess.h> | ||
48 | #include <asm/commproc.h> | ||
49 | |||
50 | /* | ||
51 | * Theory of Operation | ||
52 | * | ||
53 | * The MPC8xx CPM performs the Ethernet processing on SCC1. It can use | ||
54 | * an aribtrary number of buffers on byte boundaries, but must have at | ||
55 | * least two receive buffers to prevent constant overrun conditions. | ||
56 | * | ||
57 | * The buffer descriptors are allocated from the CPM dual port memory | ||
58 | * with the data buffers allocated from host memory, just like all other | ||
59 | * serial communication protocols. The host memory buffers are allocated | ||
60 | * from the free page pool, and then divided into smaller receive and | ||
61 | * transmit buffers. The size of the buffers should be a power of two, | ||
62 | * since that nicely divides the page. This creates a ring buffer | ||
63 | * structure similar to the LANCE and other controllers. | ||
64 | * | ||
65 | * Like the LANCE driver: | ||
66 | * The driver runs as two independent, single-threaded flows of control. One | ||
67 | * is the send-packet routine, which enforces single-threaded use by the | ||
68 | * cep->tx_busy flag. The other thread is the interrupt handler, which is | ||
69 | * single threaded by the hardware and other software. | ||
70 | * | ||
71 | * The send packet thread has partial control over the Tx ring and the | ||
72 | * 'cep->tx_busy' flag. It sets the tx_busy flag whenever it's queuing a Tx | ||
73 | * packet. If the next queue slot is empty, it clears the tx_busy flag when | ||
74 | * finished otherwise it sets the 'lp->tx_full' flag. | ||
75 | * | ||
76 | * The MBX has a control register external to the MPC8xx that has some | ||
77 | * control of the Ethernet interface. Information is in the manual for | ||
78 | * your board. | ||
79 | * | ||
80 | * The RPX boards have an external control/status register. Consult the | ||
81 | * programming documents for details unique to your board. | ||
82 | * | ||
83 | * For the TQM8xx(L) modules, there is no control register interface. | ||
84 | * All functions are directly controlled using I/O pins. See <asm/commproc.h>. | ||
85 | */ | ||
86 | |||
87 | /* The transmitter timeout | ||
88 | */ | ||
89 | #define TX_TIMEOUT (2*HZ) | ||
90 | |||
91 | /* The number of Tx and Rx buffers. These are allocated from the page | ||
92 | * pool. The code may assume these are power of two, so it is best | ||
93 | * to keep them that size. | ||
94 | * We don't need to allocate pages for the transmitter. We just use | ||
95 | * the skbuffer directly. | ||
96 | */ | ||
97 | #ifdef CONFIG_ENET_BIG_BUFFERS | ||
98 | #define CPM_ENET_RX_PAGES 32 | ||
99 | #define CPM_ENET_RX_FRSIZE 2048 | ||
100 | #define CPM_ENET_RX_FRPPG (PAGE_SIZE / CPM_ENET_RX_FRSIZE) | ||
101 | #define RX_RING_SIZE (CPM_ENET_RX_FRPPG * CPM_ENET_RX_PAGES) | ||
102 | #define TX_RING_SIZE 64 /* Must be power of two */ | ||
103 | #define TX_RING_MOD_MASK 63 /* for this to work */ | ||
104 | #else | ||
105 | #define CPM_ENET_RX_PAGES 4 | ||
106 | #define CPM_ENET_RX_FRSIZE 2048 | ||
107 | #define CPM_ENET_RX_FRPPG (PAGE_SIZE / CPM_ENET_RX_FRSIZE) | ||
108 | #define RX_RING_SIZE (CPM_ENET_RX_FRPPG * CPM_ENET_RX_PAGES) | ||
109 | #define TX_RING_SIZE 8 /* Must be power of two */ | ||
110 | #define TX_RING_MOD_MASK 7 /* for this to work */ | ||
111 | #endif | ||
112 | |||
113 | /* The CPM stores dest/src/type, data, and checksum for receive packets. | ||
114 | */ | ||
115 | #define PKT_MAXBUF_SIZE 1518 | ||
116 | #define PKT_MINBUF_SIZE 64 | ||
117 | #define PKT_MAXBLR_SIZE 1520 | ||
118 | |||
119 | /* The CPM buffer descriptors track the ring buffers. The rx_bd_base and | ||
120 | * tx_bd_base always point to the base of the buffer descriptors. The | ||
121 | * cur_rx and cur_tx point to the currently available buffer. | ||
122 | * The dirty_tx tracks the current buffer that is being sent by the | ||
123 | * controller. The cur_tx and dirty_tx are equal under both completely | ||
124 | * empty and completely full conditions. The empty/ready indicator in | ||
125 | * the buffer descriptor determines the actual condition. | ||
126 | */ | ||
127 | struct scc_enet_private { | ||
128 | /* The saved address of a sent-in-place packet/buffer, for skfree(). */ | ||
129 | struct sk_buff* tx_skbuff[TX_RING_SIZE]; | ||
130 | ushort skb_cur; | ||
131 | ushort skb_dirty; | ||
132 | |||
133 | /* CPM dual port RAM relative addresses. | ||
134 | */ | ||
135 | cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */ | ||
136 | cbd_t *tx_bd_base; | ||
137 | cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ | ||
138 | cbd_t *dirty_tx; /* The ring entries to be free()ed. */ | ||
139 | scc_t *sccp; | ||
140 | |||
141 | /* Virtual addresses for the receive buffers because we can't | ||
142 | * do a __va() on them anymore. | ||
143 | */ | ||
144 | unsigned char *rx_vaddr[RX_RING_SIZE]; | ||
145 | struct net_device_stats stats; | ||
146 | uint tx_full; | ||
147 | spinlock_t lock; | ||
148 | }; | ||
149 | |||
150 | static int scc_enet_open(struct net_device *dev); | ||
151 | static int scc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev); | ||
152 | static int scc_enet_rx(struct net_device *dev); | ||
153 | static void scc_enet_interrupt(void *dev_id, struct pt_regs *regs); | ||
154 | static int scc_enet_close(struct net_device *dev); | ||
155 | static struct net_device_stats *scc_enet_get_stats(struct net_device *dev); | ||
156 | static void set_multicast_list(struct net_device *dev); | ||
157 | |||
158 | /* Get this from various configuration locations (depends on board). | ||
159 | */ | ||
160 | /*static ushort my_enet_addr[] = { 0x0800, 0x3e26, 0x1559 };*/ | ||
161 | |||
162 | /* Typically, 860(T) boards use SCC1 for Ethernet, and other 8xx boards | ||
163 | * use SCC2. Some even may use SCC3. | ||
164 | * This is easily extended if necessary. | ||
165 | */ | ||
166 | #if defined(CONFIG_SCC3_ENET) | ||
167 | #define CPM_CR_ENET CPM_CR_CH_SCC3 | ||
168 | #define PROFF_ENET PROFF_SCC3 | ||
169 | #define SCC_ENET 2 /* Index, not number! */ | ||
170 | #define CPMVEC_ENET CPMVEC_SCC3 | ||
171 | #elif defined(CONFIG_SCC2_ENET) | ||
172 | #define CPM_CR_ENET CPM_CR_CH_SCC2 | ||
173 | #define PROFF_ENET PROFF_SCC2 | ||
174 | #define SCC_ENET 1 /* Index, not number! */ | ||
175 | #define CPMVEC_ENET CPMVEC_SCC2 | ||
176 | #elif defined(CONFIG_SCC1_ENET) | ||
177 | #define CPM_CR_ENET CPM_CR_CH_SCC1 | ||
178 | #define PROFF_ENET PROFF_SCC1 | ||
179 | #define SCC_ENET 0 /* Index, not number! */ | ||
180 | #define CPMVEC_ENET CPMVEC_SCC1 | ||
181 | #else | ||
182 | #error CONFIG_SCCx_ENET not defined | ||
183 | #endif | ||
184 | |||
185 | static int | ||
186 | scc_enet_open(struct net_device *dev) | ||
187 | { | ||
188 | |||
189 | /* I should reset the ring buffers here, but I don't yet know | ||
190 | * a simple way to do that. | ||
191 | */ | ||
192 | |||
193 | netif_start_queue(dev); | ||
194 | return 0; /* Always succeed */ | ||
195 | } | ||
196 | |||
197 | static int | ||
198 | scc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) | ||
199 | { | ||
200 | struct scc_enet_private *cep = (struct scc_enet_private *)dev->priv; | ||
201 | volatile cbd_t *bdp; | ||
202 | |||
203 | /* Fill in a Tx ring entry */ | ||
204 | bdp = cep->cur_tx; | ||
205 | |||
206 | #ifndef final_version | ||
207 | if (bdp->cbd_sc & BD_ENET_TX_READY) { | ||
208 | /* Ooops. All transmit buffers are full. Bail out. | ||
209 | * This should not happen, since cep->tx_busy should be set. | ||
210 | */ | ||
211 | printk("%s: tx queue full!.\n", dev->name); | ||
212 | return 1; | ||
213 | } | ||
214 | #endif | ||
215 | |||
216 | /* Clear all of the status flags. | ||
217 | */ | ||
218 | bdp->cbd_sc &= ~BD_ENET_TX_STATS; | ||
219 | |||
220 | /* If the frame is short, tell CPM to pad it. | ||
221 | */ | ||
222 | if (skb->len <= ETH_ZLEN) | ||
223 | bdp->cbd_sc |= BD_ENET_TX_PAD; | ||
224 | else | ||
225 | bdp->cbd_sc &= ~BD_ENET_TX_PAD; | ||
226 | |||
227 | /* Set buffer length and buffer pointer. | ||
228 | */ | ||
229 | bdp->cbd_datlen = skb->len; | ||
230 | bdp->cbd_bufaddr = __pa(skb->data); | ||
231 | |||
232 | /* Save skb pointer. | ||
233 | */ | ||
234 | cep->tx_skbuff[cep->skb_cur] = skb; | ||
235 | |||
236 | cep->stats.tx_bytes += skb->len; | ||
237 | cep->skb_cur = (cep->skb_cur+1) & TX_RING_MOD_MASK; | ||
238 | |||
239 | /* Push the data cache so the CPM does not get stale memory | ||
240 | * data. | ||
241 | */ | ||
242 | flush_dcache_range((unsigned long)(skb->data), | ||
243 | (unsigned long)(skb->data + skb->len)); | ||
244 | |||
245 | spin_lock_irq(&cep->lock); | ||
246 | |||
247 | /* Send it on its way. Tell CPM its ready, interrupt when done, | ||
248 | * its the last BD of the frame, and to put the CRC on the end. | ||
249 | */ | ||
250 | bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR | BD_ENET_TX_LAST | BD_ENET_TX_TC); | ||
251 | |||
252 | dev->trans_start = jiffies; | ||
253 | |||
254 | /* If this was the last BD in the ring, start at the beginning again. | ||
255 | */ | ||
256 | if (bdp->cbd_sc & BD_ENET_TX_WRAP) | ||
257 | bdp = cep->tx_bd_base; | ||
258 | else | ||
259 | bdp++; | ||
260 | |||
261 | if (bdp->cbd_sc & BD_ENET_TX_READY) { | ||
262 | netif_stop_queue(dev); | ||
263 | cep->tx_full = 1; | ||
264 | } | ||
265 | |||
266 | cep->cur_tx = (cbd_t *)bdp; | ||
267 | |||
268 | spin_unlock_irq(&cep->lock); | ||
269 | |||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static void | ||
274 | scc_enet_timeout(struct net_device *dev) | ||
275 | { | ||
276 | struct scc_enet_private *cep = (struct scc_enet_private *)dev->priv; | ||
277 | |||
278 | printk("%s: transmit timed out.\n", dev->name); | ||
279 | cep->stats.tx_errors++; | ||
280 | #ifndef final_version | ||
281 | { | ||
282 | int i; | ||
283 | cbd_t *bdp; | ||
284 | printk(" Ring data dump: cur_tx %p%s cur_rx %p.\n", | ||
285 | cep->cur_tx, cep->tx_full ? " (full)" : "", | ||
286 | cep->cur_rx); | ||
287 | bdp = cep->tx_bd_base; | ||
288 | for (i = 0 ; i < TX_RING_SIZE; i++, bdp++) | ||
289 | printk("%04x %04x %08x\n", | ||
290 | bdp->cbd_sc, | ||
291 | bdp->cbd_datlen, | ||
292 | bdp->cbd_bufaddr); | ||
293 | bdp = cep->rx_bd_base; | ||
294 | for (i = 0 ; i < RX_RING_SIZE; i++, bdp++) | ||
295 | printk("%04x %04x %08x\n", | ||
296 | bdp->cbd_sc, | ||
297 | bdp->cbd_datlen, | ||
298 | bdp->cbd_bufaddr); | ||
299 | } | ||
300 | #endif | ||
301 | if (!cep->tx_full) | ||
302 | netif_wake_queue(dev); | ||
303 | } | ||
304 | |||
305 | /* The interrupt handler. | ||
306 | * This is called from the CPM handler, not the MPC core interrupt. | ||
307 | */ | ||
308 | static void | ||
309 | scc_enet_interrupt(void *dev_id, struct pt_regs *regs) | ||
310 | { | ||
311 | struct net_device *dev = dev_id; | ||
312 | volatile struct scc_enet_private *cep; | ||
313 | volatile cbd_t *bdp; | ||
314 | ushort int_events; | ||
315 | int must_restart; | ||
316 | |||
317 | cep = (struct scc_enet_private *)dev->priv; | ||
318 | |||
319 | /* Get the interrupt events that caused us to be here. | ||
320 | */ | ||
321 | int_events = cep->sccp->scc_scce; | ||
322 | cep->sccp->scc_scce = int_events; | ||
323 | must_restart = 0; | ||
324 | |||
325 | /* Handle receive event in its own function. | ||
326 | */ | ||
327 | if (int_events & SCCE_ENET_RXF) | ||
328 | scc_enet_rx(dev_id); | ||
329 | |||
330 | /* Check for a transmit error. The manual is a little unclear | ||
331 | * about this, so the debug code until I get it figured out. It | ||
332 | * appears that if TXE is set, then TXB is not set. However, | ||
333 | * if carrier sense is lost during frame transmission, the TXE | ||
334 | * bit is set, "and continues the buffer transmission normally." | ||
335 | * I don't know if "normally" implies TXB is set when the buffer | ||
336 | * descriptor is closed.....trial and error :-). | ||
337 | */ | ||
338 | |||
339 | /* Transmit OK, or non-fatal error. Update the buffer descriptors. | ||
340 | */ | ||
341 | if (int_events & (SCCE_ENET_TXE | SCCE_ENET_TXB)) { | ||
342 | spin_lock(&cep->lock); | ||
343 | bdp = cep->dirty_tx; | ||
344 | while ((bdp->cbd_sc&BD_ENET_TX_READY)==0) { | ||
345 | if ((bdp==cep->cur_tx) && (cep->tx_full == 0)) | ||
346 | break; | ||
347 | |||
348 | if (bdp->cbd_sc & BD_ENET_TX_HB) /* No heartbeat */ | ||
349 | cep->stats.tx_heartbeat_errors++; | ||
350 | if (bdp->cbd_sc & BD_ENET_TX_LC) /* Late collision */ | ||
351 | cep->stats.tx_window_errors++; | ||
352 | if (bdp->cbd_sc & BD_ENET_TX_RL) /* Retrans limit */ | ||
353 | cep->stats.tx_aborted_errors++; | ||
354 | if (bdp->cbd_sc & BD_ENET_TX_UN) /* Underrun */ | ||
355 | cep->stats.tx_fifo_errors++; | ||
356 | if (bdp->cbd_sc & BD_ENET_TX_CSL) /* Carrier lost */ | ||
357 | cep->stats.tx_carrier_errors++; | ||
358 | |||
359 | |||
360 | /* No heartbeat or Lost carrier are not really bad errors. | ||
361 | * The others require a restart transmit command. | ||
362 | */ | ||
363 | if (bdp->cbd_sc & | ||
364 | (BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN)) { | ||
365 | must_restart = 1; | ||
366 | cep->stats.tx_errors++; | ||
367 | } | ||
368 | |||
369 | cep->stats.tx_packets++; | ||
370 | |||
371 | /* Deferred means some collisions occurred during transmit, | ||
372 | * but we eventually sent the packet OK. | ||
373 | */ | ||
374 | if (bdp->cbd_sc & BD_ENET_TX_DEF) | ||
375 | cep->stats.collisions++; | ||
376 | |||
377 | /* Free the sk buffer associated with this last transmit. | ||
378 | */ | ||
379 | dev_kfree_skb_irq(cep->tx_skbuff[cep->skb_dirty]); | ||
380 | cep->skb_dirty = (cep->skb_dirty + 1) & TX_RING_MOD_MASK; | ||
381 | |||
382 | /* Update pointer to next buffer descriptor to be transmitted. | ||
383 | */ | ||
384 | if (bdp->cbd_sc & BD_ENET_TX_WRAP) | ||
385 | bdp = cep->tx_bd_base; | ||
386 | else | ||
387 | bdp++; | ||
388 | |||
389 | /* I don't know if we can be held off from processing these | ||
390 | * interrupts for more than one frame time. I really hope | ||
391 | * not. In such a case, we would now want to check the | ||
392 | * currently available BD (cur_tx) and determine if any | ||
393 | * buffers between the dirty_tx and cur_tx have also been | ||
394 | * sent. We would want to process anything in between that | ||
395 | * does not have BD_ENET_TX_READY set. | ||
396 | */ | ||
397 | |||
398 | /* Since we have freed up a buffer, the ring is no longer | ||
399 | * full. | ||
400 | */ | ||
401 | if (cep->tx_full) { | ||
402 | cep->tx_full = 0; | ||
403 | if (netif_queue_stopped(dev)) | ||
404 | netif_wake_queue(dev); | ||
405 | } | ||
406 | |||
407 | cep->dirty_tx = (cbd_t *)bdp; | ||
408 | } | ||
409 | |||
410 | if (must_restart) { | ||
411 | volatile cpm8xx_t *cp; | ||
412 | |||
413 | /* Some transmit errors cause the transmitter to shut | ||
414 | * down. We now issue a restart transmit. Since the | ||
415 | * errors close the BD and update the pointers, the restart | ||
416 | * _should_ pick up without having to reset any of our | ||
417 | * pointers either. | ||
418 | */ | ||
419 | cp = cpmp; | ||
420 | cp->cp_cpcr = | ||
421 | mk_cr_cmd(CPM_CR_ENET, CPM_CR_RESTART_TX) | CPM_CR_FLG; | ||
422 | while (cp->cp_cpcr & CPM_CR_FLG); | ||
423 | } | ||
424 | spin_unlock(&cep->lock); | ||
425 | } | ||
426 | |||
427 | /* Check for receive busy, i.e. packets coming but no place to | ||
428 | * put them. This "can't happen" because the receive interrupt | ||
429 | * is tossing previous frames. | ||
430 | */ | ||
431 | if (int_events & SCCE_ENET_BSY) { | ||
432 | cep->stats.rx_dropped++; | ||
433 | printk("CPM ENET: BSY can't happen.\n"); | ||
434 | } | ||
435 | |||
436 | return; | ||
437 | } | ||
438 | |||
439 | /* During a receive, the cur_rx points to the current incoming buffer. | ||
440 | * When we update through the ring, if the next incoming buffer has | ||
441 | * not been given to the system, we just set the empty indicator, | ||
442 | * effectively tossing the packet. | ||
443 | */ | ||
444 | static int | ||
445 | scc_enet_rx(struct net_device *dev) | ||
446 | { | ||
447 | struct scc_enet_private *cep; | ||
448 | volatile cbd_t *bdp; | ||
449 | struct sk_buff *skb; | ||
450 | ushort pkt_len; | ||
451 | |||
452 | cep = (struct scc_enet_private *)dev->priv; | ||
453 | |||
454 | /* First, grab all of the stats for the incoming packet. | ||
455 | * These get messed up if we get called due to a busy condition. | ||
456 | */ | ||
457 | bdp = cep->cur_rx; | ||
458 | |||
459 | for (;;) { | ||
460 | if (bdp->cbd_sc & BD_ENET_RX_EMPTY) | ||
461 | break; | ||
462 | |||
463 | #ifndef final_version | ||
464 | /* Since we have allocated space to hold a complete frame, both | ||
465 | * the first and last indicators should be set. | ||
466 | */ | ||
467 | if ((bdp->cbd_sc & (BD_ENET_RX_FIRST | BD_ENET_RX_LAST)) != | ||
468 | (BD_ENET_RX_FIRST | BD_ENET_RX_LAST)) | ||
469 | printk("CPM ENET: rcv is not first+last\n"); | ||
470 | #endif | ||
471 | |||
472 | /* Frame too long or too short. | ||
473 | */ | ||
474 | if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH)) | ||
475 | cep->stats.rx_length_errors++; | ||
476 | if (bdp->cbd_sc & BD_ENET_RX_NO) /* Frame alignment */ | ||
477 | cep->stats.rx_frame_errors++; | ||
478 | if (bdp->cbd_sc & BD_ENET_RX_CR) /* CRC Error */ | ||
479 | cep->stats.rx_crc_errors++; | ||
480 | if (bdp->cbd_sc & BD_ENET_RX_OV) /* FIFO overrun */ | ||
481 | cep->stats.rx_crc_errors++; | ||
482 | |||
483 | /* Report late collisions as a frame error. | ||
484 | * On this error, the BD is closed, but we don't know what we | ||
485 | * have in the buffer. So, just drop this frame on the floor. | ||
486 | */ | ||
487 | if (bdp->cbd_sc & BD_ENET_RX_CL) { | ||
488 | cep->stats.rx_frame_errors++; | ||
489 | } | ||
490 | else { | ||
491 | |||
492 | /* Process the incoming frame. | ||
493 | */ | ||
494 | cep->stats.rx_packets++; | ||
495 | pkt_len = bdp->cbd_datlen; | ||
496 | cep->stats.rx_bytes += pkt_len; | ||
497 | |||
498 | /* This does 16 byte alignment, much more than we need. | ||
499 | * The packet length includes FCS, but we don't want to | ||
500 | * include that when passing upstream as it messes up | ||
501 | * bridging applications. | ||
502 | */ | ||
503 | skb = dev_alloc_skb(pkt_len-4); | ||
504 | |||
505 | if (skb == NULL) { | ||
506 | printk("%s: Memory squeeze, dropping packet.\n", dev->name); | ||
507 | cep->stats.rx_dropped++; | ||
508 | } | ||
509 | else { | ||
510 | skb->dev = dev; | ||
511 | skb_put(skb,pkt_len-4); /* Make room */ | ||
512 | eth_copy_and_sum(skb, | ||
513 | cep->rx_vaddr[bdp - cep->rx_bd_base], | ||
514 | pkt_len-4, 0); | ||
515 | skb->protocol=eth_type_trans(skb,dev); | ||
516 | netif_rx(skb); | ||
517 | } | ||
518 | } | ||
519 | |||
520 | /* Clear the status flags for this buffer. | ||
521 | */ | ||
522 | bdp->cbd_sc &= ~BD_ENET_RX_STATS; | ||
523 | |||
524 | /* Mark the buffer empty. | ||
525 | */ | ||
526 | bdp->cbd_sc |= BD_ENET_RX_EMPTY; | ||
527 | |||
528 | /* Update BD pointer to next entry. | ||
529 | */ | ||
530 | if (bdp->cbd_sc & BD_ENET_RX_WRAP) | ||
531 | bdp = cep->rx_bd_base; | ||
532 | else | ||
533 | bdp++; | ||
534 | |||
535 | } | ||
536 | cep->cur_rx = (cbd_t *)bdp; | ||
537 | |||
538 | return 0; | ||
539 | } | ||
540 | |||
541 | static int | ||
542 | scc_enet_close(struct net_device *dev) | ||
543 | { | ||
544 | /* Don't know what to do yet. | ||
545 | */ | ||
546 | netif_stop_queue(dev); | ||
547 | |||
548 | return 0; | ||
549 | } | ||
550 | |||
551 | static struct net_device_stats *scc_enet_get_stats(struct net_device *dev) | ||
552 | { | ||
553 | struct scc_enet_private *cep = (struct scc_enet_private *)dev->priv; | ||
554 | |||
555 | return &cep->stats; | ||
556 | } | ||
557 | |||
558 | /* Set or clear the multicast filter for this adaptor. | ||
559 | * Skeleton taken from sunlance driver. | ||
560 | * The CPM Ethernet implementation allows Multicast as well as individual | ||
561 | * MAC address filtering. Some of the drivers check to make sure it is | ||
562 | * a group multicast address, and discard those that are not. I guess I | ||
563 | * will do the same for now, but just remove the test if you want | ||
564 | * individual filtering as well (do the upper net layers want or support | ||
565 | * this kind of feature?). | ||
566 | */ | ||
567 | |||
568 | static void set_multicast_list(struct net_device *dev) | ||
569 | { | ||
570 | struct scc_enet_private *cep; | ||
571 | struct dev_mc_list *dmi; | ||
572 | u_char *mcptr, *tdptr; | ||
573 | volatile scc_enet_t *ep; | ||
574 | int i, j; | ||
575 | cep = (struct scc_enet_private *)dev->priv; | ||
576 | |||
577 | /* Get pointer to SCC area in parameter RAM. | ||
578 | */ | ||
579 | ep = (scc_enet_t *)dev->base_addr; | ||
580 | |||
581 | if (dev->flags&IFF_PROMISC) { | ||
582 | |||
583 | /* Log any net taps. */ | ||
584 | printk("%s: Promiscuous mode enabled.\n", dev->name); | ||
585 | cep->sccp->scc_psmr |= SCC_PSMR_PRO; | ||
586 | } else { | ||
587 | |||
588 | cep->sccp->scc_psmr &= ~SCC_PSMR_PRO; | ||
589 | |||
590 | if (dev->flags & IFF_ALLMULTI) { | ||
591 | /* Catch all multicast addresses, so set the | ||
592 | * filter to all 1's. | ||
593 | */ | ||
594 | ep->sen_gaddr1 = 0xffff; | ||
595 | ep->sen_gaddr2 = 0xffff; | ||
596 | ep->sen_gaddr3 = 0xffff; | ||
597 | ep->sen_gaddr4 = 0xffff; | ||
598 | } | ||
599 | else { | ||
600 | /* Clear filter and add the addresses in the list. | ||
601 | */ | ||
602 | ep->sen_gaddr1 = 0; | ||
603 | ep->sen_gaddr2 = 0; | ||
604 | ep->sen_gaddr3 = 0; | ||
605 | ep->sen_gaddr4 = 0; | ||
606 | |||
607 | dmi = dev->mc_list; | ||
608 | |||
609 | for (i=0; i<dev->mc_count; i++) { | ||
610 | |||
611 | /* Only support group multicast for now. | ||
612 | */ | ||
613 | if (!(dmi->dmi_addr[0] & 1)) | ||
614 | continue; | ||
615 | |||
616 | /* The address in dmi_addr is LSB first, | ||
617 | * and taddr is MSB first. We have to | ||
618 | * copy bytes MSB first from dmi_addr. | ||
619 | */ | ||
620 | mcptr = (u_char *)dmi->dmi_addr + 5; | ||
621 | tdptr = (u_char *)&ep->sen_taddrh; | ||
622 | for (j=0; j<6; j++) | ||
623 | *tdptr++ = *mcptr--; | ||
624 | |||
625 | /* Ask CPM to run CRC and set bit in | ||
626 | * filter mask. | ||
627 | */ | ||
628 | cpmp->cp_cpcr = mk_cr_cmd(CPM_CR_ENET, CPM_CR_SET_GADDR) | CPM_CR_FLG; | ||
629 | /* this delay is necessary here -- Cort */ | ||
630 | udelay(10); | ||
631 | while (cpmp->cp_cpcr & CPM_CR_FLG); | ||
632 | } | ||
633 | } | ||
634 | } | ||
635 | } | ||
636 | |||
637 | /* Initialize the CPM Ethernet on SCC. If EPPC-Bug loaded us, or performed | ||
638 | * some other network I/O, a whole bunch of this has already been set up. | ||
639 | * It is no big deal if we do it again, we just have to disable the | ||
640 | * transmit and receive to make sure we don't catch the CPM with some | ||
641 | * inconsistent control information. | ||
642 | */ | ||
643 | static int __init scc_enet_init(void) | ||
644 | { | ||
645 | struct net_device *dev; | ||
646 | struct scc_enet_private *cep; | ||
647 | int i, j, k, err; | ||
648 | uint dp_offset; | ||
649 | unsigned char *eap, *ba; | ||
650 | dma_addr_t mem_addr; | ||
651 | bd_t *bd; | ||
652 | volatile cbd_t *bdp; | ||
653 | volatile cpm8xx_t *cp; | ||
654 | volatile scc_t *sccp; | ||
655 | volatile scc_enet_t *ep; | ||
656 | volatile immap_t *immap; | ||
657 | |||
658 | cp = cpmp; /* Get pointer to Communication Processor */ | ||
659 | |||
660 | immap = (immap_t *)(mfspr(SPRN_IMMR) & 0xFFFF0000); /* and to internal registers */ | ||
661 | |||
662 | bd = (bd_t *)__res; | ||
663 | |||
664 | dev = alloc_etherdev(sizeof(*cep)); | ||
665 | if (!dev) | ||
666 | return -ENOMEM; | ||
667 | |||
668 | cep = dev->priv; | ||
669 | spin_lock_init(&cep->lock); | ||
670 | |||
671 | /* Get pointer to SCC area in parameter RAM. | ||
672 | */ | ||
673 | ep = (scc_enet_t *)(&cp->cp_dparam[PROFF_ENET]); | ||
674 | |||
675 | /* And another to the SCC register area. | ||
676 | */ | ||
677 | sccp = (volatile scc_t *)(&cp->cp_scc[SCC_ENET]); | ||
678 | cep->sccp = (scc_t *)sccp; /* Keep the pointer handy */ | ||
679 | |||
680 | /* Disable receive and transmit in case EPPC-Bug started it. | ||
681 | */ | ||
682 | sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); | ||
683 | |||
684 | /* Cookbook style from the MPC860 manual..... | ||
685 | * Not all of this is necessary if EPPC-Bug has initialized | ||
686 | * the network. | ||
687 | * So far we are lucky, all board configurations use the same | ||
688 | * pins, or at least the same I/O Port for these functions..... | ||
689 | * It can't last though...... | ||
690 | */ | ||
691 | |||
692 | #if (defined(PA_ENET_RXD) && defined(PA_ENET_TXD)) | ||
693 | /* Configure port A pins for Txd and Rxd. | ||
694 | */ | ||
695 | immap->im_ioport.iop_papar |= (PA_ENET_RXD | PA_ENET_TXD); | ||
696 | immap->im_ioport.iop_padir &= ~(PA_ENET_RXD | PA_ENET_TXD); | ||
697 | immap->im_ioport.iop_paodr &= ~PA_ENET_TXD; | ||
698 | #elif (defined(PB_ENET_RXD) && defined(PB_ENET_TXD)) | ||
699 | /* Configure port B pins for Txd and Rxd. | ||
700 | */ | ||
701 | immap->im_cpm.cp_pbpar |= (PB_ENET_RXD | PB_ENET_TXD); | ||
702 | immap->im_cpm.cp_pbdir &= ~(PB_ENET_RXD | PB_ENET_TXD); | ||
703 | immap->im_cpm.cp_pbodr &= ~PB_ENET_TXD; | ||
704 | #else | ||
705 | #error Exactly ONE pair of PA_ENET_[RT]XD, PB_ENET_[RT]XD must be defined | ||
706 | #endif | ||
707 | |||
708 | #if defined(PC_ENET_LBK) | ||
709 | /* Configure port C pins to disable External Loopback | ||
710 | */ | ||
711 | immap->im_ioport.iop_pcpar &= ~PC_ENET_LBK; | ||
712 | immap->im_ioport.iop_pcdir |= PC_ENET_LBK; | ||
713 | immap->im_ioport.iop_pcso &= ~PC_ENET_LBK; | ||
714 | immap->im_ioport.iop_pcdat &= ~PC_ENET_LBK; /* Disable Loopback */ | ||
715 | #endif /* PC_ENET_LBK */ | ||
716 | |||
717 | /* Configure port C pins to enable CLSN and RENA. | ||
718 | */ | ||
719 | immap->im_ioport.iop_pcpar &= ~(PC_ENET_CLSN | PC_ENET_RENA); | ||
720 | immap->im_ioport.iop_pcdir &= ~(PC_ENET_CLSN | PC_ENET_RENA); | ||
721 | immap->im_ioport.iop_pcso |= (PC_ENET_CLSN | PC_ENET_RENA); | ||
722 | |||
723 | /* Configure port A for TCLK and RCLK. | ||
724 | */ | ||
725 | immap->im_ioport.iop_papar |= (PA_ENET_TCLK | PA_ENET_RCLK); | ||
726 | immap->im_ioport.iop_padir &= ~(PA_ENET_TCLK | PA_ENET_RCLK); | ||
727 | |||
728 | /* Configure Serial Interface clock routing. | ||
729 | * First, clear all SCC bits to zero, then set the ones we want. | ||
730 | */ | ||
731 | cp->cp_sicr &= ~SICR_ENET_MASK; | ||
732 | cp->cp_sicr |= SICR_ENET_CLKRT; | ||
733 | |||
734 | /* Manual says set SDDR, but I can't find anything with that | ||
735 | * name. I think it is a misprint, and should be SDCR. This | ||
736 | * has already been set by the communication processor initialization. | ||
737 | */ | ||
738 | |||
739 | /* Allocate space for the buffer descriptors in the DP ram. | ||
740 | * These are relative offsets in the DP ram address space. | ||
741 | * Initialize base addresses for the buffer descriptors. | ||
742 | */ | ||
743 | dp_offset = cpm_dpalloc(sizeof(cbd_t) * RX_RING_SIZE, 8); | ||
744 | ep->sen_genscc.scc_rbase = dp_offset; | ||
745 | cep->rx_bd_base = cpm_dpram_addr(dp_offset); | ||
746 | |||
747 | dp_offset = cpm_dpalloc(sizeof(cbd_t) * TX_RING_SIZE, 8); | ||
748 | ep->sen_genscc.scc_tbase = dp_offset; | ||
749 | cep->tx_bd_base = cpm_dpram_addr(dp_offset); | ||
750 | |||
751 | cep->dirty_tx = cep->cur_tx = cep->tx_bd_base; | ||
752 | cep->cur_rx = cep->rx_bd_base; | ||
753 | |||
754 | /* Issue init Rx BD command for SCC. | ||
755 | * Manual says to perform an Init Rx parameters here. We have | ||
756 | * to perform both Rx and Tx because the SCC may have been | ||
757 | * already running. | ||
758 | * In addition, we have to do it later because we don't yet have | ||
759 | * all of the BD control/status set properly. | ||
760 | cp->cp_cpcr = mk_cr_cmd(CPM_CR_ENET, CPM_CR_INIT_RX) | CPM_CR_FLG; | ||
761 | while (cp->cp_cpcr & CPM_CR_FLG); | ||
762 | */ | ||
763 | |||
764 | /* Initialize function code registers for big-endian. | ||
765 | */ | ||
766 | ep->sen_genscc.scc_rfcr = SCC_EB; | ||
767 | ep->sen_genscc.scc_tfcr = SCC_EB; | ||
768 | |||
769 | /* Set maximum bytes per receive buffer. | ||
770 | * This appears to be an Ethernet frame size, not the buffer | ||
771 | * fragment size. It must be a multiple of four. | ||
772 | */ | ||
773 | ep->sen_genscc.scc_mrblr = PKT_MAXBLR_SIZE; | ||
774 | |||
775 | /* Set CRC preset and mask. | ||
776 | */ | ||
777 | ep->sen_cpres = 0xffffffff; | ||
778 | ep->sen_cmask = 0xdebb20e3; | ||
779 | |||
780 | ep->sen_crcec = 0; /* CRC Error counter */ | ||
781 | ep->sen_alec = 0; /* alignment error counter */ | ||
782 | ep->sen_disfc = 0; /* discard frame counter */ | ||
783 | |||
784 | ep->sen_pads = 0x8888; /* Tx short frame pad character */ | ||
785 | ep->sen_retlim = 15; /* Retry limit threshold */ | ||
786 | |||
787 | ep->sen_maxflr = PKT_MAXBUF_SIZE; /* maximum frame length register */ | ||
788 | ep->sen_minflr = PKT_MINBUF_SIZE; /* minimum frame length register */ | ||
789 | |||
790 | ep->sen_maxd1 = PKT_MAXBLR_SIZE; /* maximum DMA1 length */ | ||
791 | ep->sen_maxd2 = PKT_MAXBLR_SIZE; /* maximum DMA2 length */ | ||
792 | |||
793 | /* Clear hash tables. | ||
794 | */ | ||
795 | ep->sen_gaddr1 = 0; | ||
796 | ep->sen_gaddr2 = 0; | ||
797 | ep->sen_gaddr3 = 0; | ||
798 | ep->sen_gaddr4 = 0; | ||
799 | ep->sen_iaddr1 = 0; | ||
800 | ep->sen_iaddr2 = 0; | ||
801 | ep->sen_iaddr3 = 0; | ||
802 | ep->sen_iaddr4 = 0; | ||
803 | |||
804 | /* Set Ethernet station address. | ||
805 | */ | ||
806 | eap = (unsigned char *)&(ep->sen_paddrh); | ||
807 | for (i=5; i>=0; i--) | ||
808 | *eap++ = dev->dev_addr[i] = bd->bi_enetaddr[i]; | ||
809 | |||
810 | ep->sen_pper = 0; /* 'cause the book says so */ | ||
811 | ep->sen_taddrl = 0; /* temp address (LSB) */ | ||
812 | ep->sen_taddrm = 0; | ||
813 | ep->sen_taddrh = 0; /* temp address (MSB) */ | ||
814 | |||
815 | /* Now allocate the host memory pages and initialize the | ||
816 | * buffer descriptors. | ||
817 | */ | ||
818 | bdp = cep->tx_bd_base; | ||
819 | for (i=0; i<TX_RING_SIZE; i++) { | ||
820 | |||
821 | /* Initialize the BD for every fragment in the page. | ||
822 | */ | ||
823 | bdp->cbd_sc = 0; | ||
824 | bdp->cbd_bufaddr = 0; | ||
825 | bdp++; | ||
826 | } | ||
827 | |||
828 | /* Set the last buffer to wrap. | ||
829 | */ | ||
830 | bdp--; | ||
831 | bdp->cbd_sc |= BD_SC_WRAP; | ||
832 | |||
833 | bdp = cep->rx_bd_base; | ||
834 | k = 0; | ||
835 | for (i=0; i<CPM_ENET_RX_PAGES; i++) { | ||
836 | |||
837 | /* Allocate a page. | ||
838 | */ | ||
839 | ba = (unsigned char *)dma_alloc_coherent(NULL, PAGE_SIZE, | ||
840 | &mem_addr, GFP_KERNEL); | ||
841 | /* BUG: no check for failure */ | ||
842 | |||
843 | /* Initialize the BD for every fragment in the page. | ||
844 | */ | ||
845 | for (j=0; j<CPM_ENET_RX_FRPPG; j++) { | ||
846 | bdp->cbd_sc = BD_ENET_RX_EMPTY | BD_ENET_RX_INTR; | ||
847 | bdp->cbd_bufaddr = mem_addr; | ||
848 | cep->rx_vaddr[k++] = ba; | ||
849 | mem_addr += CPM_ENET_RX_FRSIZE; | ||
850 | ba += CPM_ENET_RX_FRSIZE; | ||
851 | bdp++; | ||
852 | } | ||
853 | } | ||
854 | |||
855 | /* Set the last buffer to wrap. | ||
856 | */ | ||
857 | bdp--; | ||
858 | bdp->cbd_sc |= BD_SC_WRAP; | ||
859 | |||
860 | /* Let's re-initialize the channel now. We have to do it later | ||
861 | * than the manual describes because we have just now finished | ||
862 | * the BD initialization. | ||
863 | */ | ||
864 | cp->cp_cpcr = mk_cr_cmd(CPM_CR_ENET, CPM_CR_INIT_TRX) | CPM_CR_FLG; | ||
865 | while (cp->cp_cpcr & CPM_CR_FLG); | ||
866 | |||
867 | cep->skb_cur = cep->skb_dirty = 0; | ||
868 | |||
869 | sccp->scc_scce = 0xffff; /* Clear any pending events */ | ||
870 | |||
871 | /* Enable interrupts for transmit error, complete frame | ||
872 | * received, and any transmit buffer we have also set the | ||
873 | * interrupt flag. | ||
874 | */ | ||
875 | sccp->scc_sccm = (SCCE_ENET_TXE | SCCE_ENET_RXF | SCCE_ENET_TXB); | ||
876 | |||
877 | /* Install our interrupt handler. | ||
878 | */ | ||
879 | cpm_install_handler(CPMVEC_ENET, scc_enet_interrupt, dev); | ||
880 | |||
881 | /* Set GSMR_H to enable all normal operating modes. | ||
882 | * Set GSMR_L to enable Ethernet to MC68160. | ||
883 | */ | ||
884 | sccp->scc_gsmrh = 0; | ||
885 | sccp->scc_gsmrl = (SCC_GSMRL_TCI | SCC_GSMRL_TPL_48 | SCC_GSMRL_TPP_10 | SCC_GSMRL_MODE_ENET); | ||
886 | |||
887 | /* Set sync/delimiters. | ||
888 | */ | ||
889 | sccp->scc_dsr = 0xd555; | ||
890 | |||
891 | /* Set processing mode. Use Ethernet CRC, catch broadcast, and | ||
892 | * start frame search 22 bit times after RENA. | ||
893 | */ | ||
894 | sccp->scc_psmr = (SCC_PSMR_ENCRC | SCC_PSMR_NIB22); | ||
895 | |||
896 | /* It is now OK to enable the Ethernet transmitter. | ||
897 | * Unfortunately, there are board implementation differences here. | ||
898 | */ | ||
899 | #if (!defined (PB_ENET_TENA) && defined (PC_ENET_TENA)) | ||
900 | immap->im_ioport.iop_pcpar |= PC_ENET_TENA; | ||
901 | immap->im_ioport.iop_pcdir &= ~PC_ENET_TENA; | ||
902 | #elif ( defined (PB_ENET_TENA) && !defined (PC_ENET_TENA)) | ||
903 | cp->cp_pbpar |= PB_ENET_TENA; | ||
904 | cp->cp_pbdir |= PB_ENET_TENA; | ||
905 | #else | ||
906 | #error Configuration Error: define exactly ONE of PB_ENET_TENA, PC_ENET_TENA | ||
907 | #endif | ||
908 | |||
909 | #if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) | ||
910 | /* And while we are here, set the configuration to enable ethernet. | ||
911 | */ | ||
912 | *((volatile uint *)RPX_CSR_ADDR) &= ~BCSR0_ETHLPBK; | ||
913 | *((volatile uint *)RPX_CSR_ADDR) |= | ||
914 | (BCSR0_ETHEN | BCSR0_COLTESTDIS | BCSR0_FULLDPLXDIS); | ||
915 | #endif | ||
916 | |||
917 | #ifdef CONFIG_BSEIP | ||
918 | /* BSE uses port B and C for PHY control. | ||
919 | */ | ||
920 | cp->cp_pbpar &= ~(PB_BSE_POWERUP | PB_BSE_FDXDIS); | ||
921 | cp->cp_pbdir |= (PB_BSE_POWERUP | PB_BSE_FDXDIS); | ||
922 | cp->cp_pbdat |= (PB_BSE_POWERUP | PB_BSE_FDXDIS); | ||
923 | |||
924 | immap->im_ioport.iop_pcpar &= ~PC_BSE_LOOPBACK; | ||
925 | immap->im_ioport.iop_pcdir |= PC_BSE_LOOPBACK; | ||
926 | immap->im_ioport.iop_pcso &= ~PC_BSE_LOOPBACK; | ||
927 | immap->im_ioport.iop_pcdat &= ~PC_BSE_LOOPBACK; | ||
928 | #endif | ||
929 | |||
930 | #ifdef CONFIG_FADS | ||
931 | cp->cp_pbpar |= PB_ENET_TENA; | ||
932 | cp->cp_pbdir |= PB_ENET_TENA; | ||
933 | |||
934 | /* Enable the EEST PHY. | ||
935 | */ | ||
936 | *((volatile uint *)BCSR1) &= ~BCSR1_ETHEN; | ||
937 | #endif | ||
938 | |||
939 | dev->base_addr = (unsigned long)ep; | ||
940 | #if 0 | ||
941 | dev->name = "CPM_ENET"; | ||
942 | #endif | ||
943 | |||
944 | /* The CPM Ethernet specific entries in the device structure. */ | ||
945 | dev->open = scc_enet_open; | ||
946 | dev->hard_start_xmit = scc_enet_start_xmit; | ||
947 | dev->tx_timeout = scc_enet_timeout; | ||
948 | dev->watchdog_timeo = TX_TIMEOUT; | ||
949 | dev->stop = scc_enet_close; | ||
950 | dev->get_stats = scc_enet_get_stats; | ||
951 | dev->set_multicast_list = set_multicast_list; | ||
952 | |||
953 | err = register_netdev(dev); | ||
954 | if (err) { | ||
955 | free_netdev(dev); | ||
956 | return err; | ||
957 | } | ||
958 | |||
959 | /* And last, enable the transmit and receive processing. | ||
960 | */ | ||
961 | sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); | ||
962 | |||
963 | printk("%s: CPM ENET Version 0.2 on SCC%d, ", dev->name, SCC_ENET+1); | ||
964 | for (i=0; i<5; i++) | ||
965 | printk("%02x:", dev->dev_addr[i]); | ||
966 | printk("%02x\n", dev->dev_addr[5]); | ||
967 | |||
968 | return 0; | ||
969 | } | ||
970 | |||
971 | module_init(scc_enet_init); | ||
diff --git a/arch/ppc/8xx_io/fec.c b/arch/ppc/8xx_io/fec.c new file mode 100644 index 000000000000..0730392dcc20 --- /dev/null +++ b/arch/ppc/8xx_io/fec.c | |||
@@ -0,0 +1,1973 @@ | |||
1 | /* | ||
2 | * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx. | ||
3 | * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) | ||
4 | * | ||
5 | * This version of the driver is specific to the FADS implementation, | ||
6 | * since the board contains control registers external to the processor | ||
7 | * for the control of the LevelOne LXT970 transceiver. The MPC860T manual | ||
8 | * describes connections using the internal parallel port I/O, which | ||
9 | * is basically all of Port D. | ||
10 | * | ||
11 | * Includes support for the following PHYs: QS6612, LXT970, LXT971/2. | ||
12 | * | ||
13 | * Right now, I am very wasteful with the buffers. I allocate memory | ||
14 | * pages and then divide them into 2K frame buffers. This way I know I | ||
15 | * have buffers large enough to hold one frame within one buffer descriptor. | ||
16 | * Once I get this working, I will use 64 or 128 byte CPM buffers, which | ||
17 | * will be much more memory efficient and will easily handle lots of | ||
18 | * small packets. | ||
19 | * | ||
20 | * Much better multiple PHY support by Magnus Damm. | ||
21 | * Copyright (c) 2000 Ericsson Radio Systems AB. | ||
22 | * | ||
23 | * Make use of MII for PHY control configurable. | ||
24 | * Some fixes. | ||
25 | * Copyright (c) 2000-2002 Wolfgang Denk, DENX Software Engineering. | ||
26 | * | ||
27 | * Support for AMD AM79C874 added. | ||
28 | * Thomas Lange, thomas@corelatus.com | ||
29 | */ | ||
30 | |||
31 | #include <linux/config.h> | ||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/sched.h> | ||
34 | #include <linux/string.h> | ||
35 | #include <linux/ptrace.h> | ||
36 | #include <linux/errno.h> | ||
37 | #include <linux/ioport.h> | ||
38 | #include <linux/slab.h> | ||
39 | #include <linux/interrupt.h> | ||
40 | #include <linux/pci.h> | ||
41 | #include <linux/init.h> | ||
42 | #include <linux/delay.h> | ||
43 | #include <linux/netdevice.h> | ||
44 | #include <linux/etherdevice.h> | ||
45 | #include <linux/skbuff.h> | ||
46 | #include <linux/spinlock.h> | ||
47 | #include <linux/bitops.h> | ||
48 | #ifdef CONFIG_FEC_PACKETHOOK | ||
49 | #include <linux/pkthook.h> | ||
50 | #endif | ||
51 | |||
52 | #include <asm/8xx_immap.h> | ||
53 | #include <asm/pgtable.h> | ||
54 | #include <asm/mpc8xx.h> | ||
55 | #include <asm/irq.h> | ||
56 | #include <asm/uaccess.h> | ||
57 | #include <asm/commproc.h> | ||
58 | |||
59 | #ifdef CONFIG_USE_MDIO | ||
60 | /* Forward declarations of some structures to support different PHYs | ||
61 | */ | ||
62 | |||
63 | typedef struct { | ||
64 | uint mii_data; | ||
65 | void (*funct)(uint mii_reg, struct net_device *dev); | ||
66 | } phy_cmd_t; | ||
67 | |||
68 | typedef struct { | ||
69 | uint id; | ||
70 | char *name; | ||
71 | |||
72 | const phy_cmd_t *config; | ||
73 | const phy_cmd_t *startup; | ||
74 | const phy_cmd_t *ack_int; | ||
75 | const phy_cmd_t *shutdown; | ||
76 | } phy_info_t; | ||
77 | #endif /* CONFIG_USE_MDIO */ | ||
78 | |||
79 | /* The number of Tx and Rx buffers. These are allocated from the page | ||
80 | * pool. The code may assume these are power of two, so it is best | ||
81 | * to keep them that size. | ||
82 | * We don't need to allocate pages for the transmitter. We just use | ||
83 | * the skbuffer directly. | ||
84 | */ | ||
85 | #ifdef CONFIG_ENET_BIG_BUFFERS | ||
86 | #define FEC_ENET_RX_PAGES 16 | ||
87 | #define FEC_ENET_RX_FRSIZE 2048 | ||
88 | #define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE) | ||
89 | #define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES) | ||
90 | #define TX_RING_SIZE 16 /* Must be power of two */ | ||
91 | #define TX_RING_MOD_MASK 15 /* for this to work */ | ||
92 | #else | ||
93 | #define FEC_ENET_RX_PAGES 4 | ||
94 | #define FEC_ENET_RX_FRSIZE 2048 | ||
95 | #define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE) | ||
96 | #define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES) | ||
97 | #define TX_RING_SIZE 8 /* Must be power of two */ | ||
98 | #define TX_RING_MOD_MASK 7 /* for this to work */ | ||
99 | #endif | ||
100 | |||
101 | /* Interrupt events/masks. | ||
102 | */ | ||
103 | #define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */ | ||
104 | #define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */ | ||
105 | #define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */ | ||
106 | #define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */ | ||
107 | #define FEC_ENET_TXF ((uint)0x08000000) /* Full frame transmitted */ | ||
108 | #define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */ | ||
109 | #define FEC_ENET_RXF ((uint)0x02000000) /* Full frame received */ | ||
110 | #define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ | ||
111 | #define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ | ||
112 | #define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ | ||
113 | |||
114 | /* | ||
115 | */ | ||
116 | #define FEC_ECNTRL_PINMUX 0x00000004 | ||
117 | #define FEC_ECNTRL_ETHER_EN 0x00000002 | ||
118 | #define FEC_ECNTRL_RESET 0x00000001 | ||
119 | |||
120 | #define FEC_RCNTRL_BC_REJ 0x00000010 | ||
121 | #define FEC_RCNTRL_PROM 0x00000008 | ||
122 | #define FEC_RCNTRL_MII_MODE 0x00000004 | ||
123 | #define FEC_RCNTRL_DRT 0x00000002 | ||
124 | #define FEC_RCNTRL_LOOP 0x00000001 | ||
125 | |||
126 | #define FEC_TCNTRL_FDEN 0x00000004 | ||
127 | #define FEC_TCNTRL_HBC 0x00000002 | ||
128 | #define FEC_TCNTRL_GTS 0x00000001 | ||
129 | |||
130 | /* Delay to wait for FEC reset command to complete (in us) | ||
131 | */ | ||
132 | #define FEC_RESET_DELAY 50 | ||
133 | |||
134 | /* The FEC stores dest/src/type, data, and checksum for receive packets. | ||
135 | */ | ||
136 | #define PKT_MAXBUF_SIZE 1518 | ||
137 | #define PKT_MINBUF_SIZE 64 | ||
138 | #define PKT_MAXBLR_SIZE 1520 | ||
139 | |||
140 | /* The FEC buffer descriptors track the ring buffers. The rx_bd_base and | ||
141 | * tx_bd_base always point to the base of the buffer descriptors. The | ||
142 | * cur_rx and cur_tx point to the currently available buffer. | ||
143 | * The dirty_tx tracks the current buffer that is being sent by the | ||
144 | * controller. The cur_tx and dirty_tx are equal under both completely | ||
145 | * empty and completely full conditions. The empty/ready indicator in | ||
146 | * the buffer descriptor determines the actual condition. | ||
147 | */ | ||
148 | struct fec_enet_private { | ||
149 | /* The saved address of a sent-in-place packet/buffer, for skfree(). */ | ||
150 | struct sk_buff* tx_skbuff[TX_RING_SIZE]; | ||
151 | ushort skb_cur; | ||
152 | ushort skb_dirty; | ||
153 | |||
154 | /* CPM dual port RAM relative addresses. | ||
155 | */ | ||
156 | cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */ | ||
157 | cbd_t *tx_bd_base; | ||
158 | cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ | ||
159 | cbd_t *dirty_tx; /* The ring entries to be free()ed. */ | ||
160 | |||
161 | /* Virtual addresses for the receive buffers because we can't | ||
162 | * do a __va() on them anymore. | ||
163 | */ | ||
164 | unsigned char *rx_vaddr[RX_RING_SIZE]; | ||
165 | |||
166 | struct net_device_stats stats; | ||
167 | uint tx_full; | ||
168 | spinlock_t lock; | ||
169 | |||
170 | #ifdef CONFIG_USE_MDIO | ||
171 | uint phy_id; | ||
172 | uint phy_id_done; | ||
173 | uint phy_status; | ||
174 | uint phy_speed; | ||
175 | phy_info_t *phy; | ||
176 | struct tq_struct phy_task; | ||
177 | |||
178 | uint sequence_done; | ||
179 | |||
180 | uint phy_addr; | ||
181 | #endif /* CONFIG_USE_MDIO */ | ||
182 | |||
183 | int link; | ||
184 | int old_link; | ||
185 | int full_duplex; | ||
186 | |||
187 | #ifdef CONFIG_FEC_PACKETHOOK | ||
188 | unsigned long ph_lock; | ||
189 | fec_ph_func *ph_rxhandler; | ||
190 | fec_ph_func *ph_txhandler; | ||
191 | __u16 ph_proto; | ||
192 | volatile __u32 *ph_regaddr; | ||
193 | void *ph_priv; | ||
194 | #endif | ||
195 | }; | ||
196 | |||
197 | static int fec_enet_open(struct net_device *dev); | ||
198 | static int fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev); | ||
199 | #ifdef CONFIG_USE_MDIO | ||
200 | static void fec_enet_mii(struct net_device *dev); | ||
201 | #endif /* CONFIG_USE_MDIO */ | ||
202 | static void fec_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs); | ||
203 | #ifdef CONFIG_FEC_PACKETHOOK | ||
204 | static void fec_enet_tx(struct net_device *dev, __u32 regval); | ||
205 | static void fec_enet_rx(struct net_device *dev, __u32 regval); | ||
206 | #else | ||
207 | static void fec_enet_tx(struct net_device *dev); | ||
208 | static void fec_enet_rx(struct net_device *dev); | ||
209 | #endif | ||
210 | static int fec_enet_close(struct net_device *dev); | ||
211 | static struct net_device_stats *fec_enet_get_stats(struct net_device *dev); | ||
212 | static void set_multicast_list(struct net_device *dev); | ||
213 | static void fec_restart(struct net_device *dev, int duplex); | ||
214 | static void fec_stop(struct net_device *dev); | ||
215 | static ushort my_enet_addr[3]; | ||
216 | |||
217 | #ifdef CONFIG_USE_MDIO | ||
218 | /* MII processing. We keep this as simple as possible. Requests are | ||
219 | * placed on the list (if there is room). When the request is finished | ||
220 | * by the MII, an optional function may be called. | ||
221 | */ | ||
222 | typedef struct mii_list { | ||
223 | uint mii_regval; | ||
224 | void (*mii_func)(uint val, struct net_device *dev); | ||
225 | struct mii_list *mii_next; | ||
226 | } mii_list_t; | ||
227 | |||
228 | #define NMII 20 | ||
229 | mii_list_t mii_cmds[NMII]; | ||
230 | mii_list_t *mii_free; | ||
231 | mii_list_t *mii_head; | ||
232 | mii_list_t *mii_tail; | ||
233 | |||
234 | static int mii_queue(struct net_device *dev, int request, | ||
235 | void (*func)(uint, struct net_device *)); | ||
236 | |||
237 | /* Make MII read/write commands for the FEC. | ||
238 | */ | ||
239 | #define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18)) | ||
240 | #define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | \ | ||
241 | (VAL & 0xffff)) | ||
242 | #define mk_mii_end 0 | ||
243 | #endif /* CONFIG_USE_MDIO */ | ||
244 | |||
245 | /* Transmitter timeout. | ||
246 | */ | ||
247 | #define TX_TIMEOUT (2*HZ) | ||
248 | |||
249 | #ifdef CONFIG_USE_MDIO | ||
250 | /* Register definitions for the PHY. | ||
251 | */ | ||
252 | |||
253 | #define MII_REG_CR 0 /* Control Register */ | ||
254 | #define MII_REG_SR 1 /* Status Register */ | ||
255 | #define MII_REG_PHYIR1 2 /* PHY Identification Register 1 */ | ||
256 | #define MII_REG_PHYIR2 3 /* PHY Identification Register 2 */ | ||
257 | #define MII_REG_ANAR 4 /* A-N Advertisement Register */ | ||
258 | #define MII_REG_ANLPAR 5 /* A-N Link Partner Ability Register */ | ||
259 | #define MII_REG_ANER 6 /* A-N Expansion Register */ | ||
260 | #define MII_REG_ANNPTR 7 /* A-N Next Page Transmit Register */ | ||
261 | #define MII_REG_ANLPRNPR 8 /* A-N Link Partner Received Next Page Reg. */ | ||
262 | |||
263 | /* values for phy_status */ | ||
264 | |||
265 | #define PHY_CONF_ANE 0x0001 /* 1 auto-negotiation enabled */ | ||
266 | #define PHY_CONF_LOOP 0x0002 /* 1 loopback mode enabled */ | ||
267 | #define PHY_CONF_SPMASK 0x00f0 /* mask for speed */ | ||
268 | #define PHY_CONF_10HDX 0x0010 /* 10 Mbit half duplex supported */ | ||
269 | #define PHY_CONF_10FDX 0x0020 /* 10 Mbit full duplex supported */ | ||
270 | #define PHY_CONF_100HDX 0x0040 /* 100 Mbit half duplex supported */ | ||
271 | #define PHY_CONF_100FDX 0x0080 /* 100 Mbit full duplex supported */ | ||
272 | |||
273 | #define PHY_STAT_LINK 0x0100 /* 1 up - 0 down */ | ||
274 | #define PHY_STAT_FAULT 0x0200 /* 1 remote fault */ | ||
275 | #define PHY_STAT_ANC 0x0400 /* 1 auto-negotiation complete */ | ||
276 | #define PHY_STAT_SPMASK 0xf000 /* mask for speed */ | ||
277 | #define PHY_STAT_10HDX 0x1000 /* 10 Mbit half duplex selected */ | ||
278 | #define PHY_STAT_10FDX 0x2000 /* 10 Mbit full duplex selected */ | ||
279 | #define PHY_STAT_100HDX 0x4000 /* 100 Mbit half duplex selected */ | ||
280 | #define PHY_STAT_100FDX 0x8000 /* 100 Mbit full duplex selected */ | ||
281 | #endif /* CONFIG_USE_MDIO */ | ||
282 | |||
283 | #ifdef CONFIG_FEC_PACKETHOOK | ||
284 | int | ||
285 | fec_register_ph(struct net_device *dev, fec_ph_func *rxfun, fec_ph_func *txfun, | ||
286 | __u16 proto, volatile __u32 *regaddr, void *priv) | ||
287 | { | ||
288 | struct fec_enet_private *fep; | ||
289 | int retval = 0; | ||
290 | |||
291 | fep = dev->priv; | ||
292 | |||
293 | if (test_and_set_bit(0, (void*)&fep->ph_lock) != 0) { | ||
294 | /* Someone is messing with the packet hook */ | ||
295 | return -EAGAIN; | ||
296 | } | ||
297 | if (fep->ph_rxhandler != NULL || fep->ph_txhandler != NULL) { | ||
298 | retval = -EBUSY; | ||
299 | goto out; | ||
300 | } | ||
301 | fep->ph_rxhandler = rxfun; | ||
302 | fep->ph_txhandler = txfun; | ||
303 | fep->ph_proto = proto; | ||
304 | fep->ph_regaddr = regaddr; | ||
305 | fep->ph_priv = priv; | ||
306 | |||
307 | out: | ||
308 | fep->ph_lock = 0; | ||
309 | |||
310 | return retval; | ||
311 | } | ||
312 | |||
313 | |||
314 | int | ||
315 | fec_unregister_ph(struct net_device *dev) | ||
316 | { | ||
317 | struct fec_enet_private *fep; | ||
318 | int retval = 0; | ||
319 | |||
320 | fep = dev->priv; | ||
321 | |||
322 | if (test_and_set_bit(0, (void*)&fep->ph_lock) != 0) { | ||
323 | /* Someone is messing with the packet hook */ | ||
324 | return -EAGAIN; | ||
325 | } | ||
326 | |||
327 | fep->ph_rxhandler = fep->ph_txhandler = NULL; | ||
328 | fep->ph_proto = 0; | ||
329 | fep->ph_regaddr = NULL; | ||
330 | fep->ph_priv = NULL; | ||
331 | |||
332 | fep->ph_lock = 0; | ||
333 | |||
334 | return retval; | ||
335 | } | ||
336 | |||
337 | EXPORT_SYMBOL(fec_register_ph); | ||
338 | EXPORT_SYMBOL(fec_unregister_ph); | ||
339 | |||
340 | #endif /* CONFIG_FEC_PACKETHOOK */ | ||
341 | |||
342 | static int | ||
343 | fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) | ||
344 | { | ||
345 | struct fec_enet_private *fep; | ||
346 | volatile fec_t *fecp; | ||
347 | volatile cbd_t *bdp; | ||
348 | |||
349 | fep = dev->priv; | ||
350 | fecp = (volatile fec_t*)dev->base_addr; | ||
351 | |||
352 | if (!fep->link) { | ||
353 | /* Link is down or autonegotiation is in progress. */ | ||
354 | return 1; | ||
355 | } | ||
356 | |||
357 | /* Fill in a Tx ring entry */ | ||
358 | bdp = fep->cur_tx; | ||
359 | |||
360 | #ifndef final_version | ||
361 | if (bdp->cbd_sc & BD_ENET_TX_READY) { | ||
362 | /* Ooops. All transmit buffers are full. Bail out. | ||
363 | * This should not happen, since dev->tbusy should be set. | ||
364 | */ | ||
365 | printk("%s: tx queue full!.\n", dev->name); | ||
366 | return 1; | ||
367 | } | ||
368 | #endif | ||
369 | |||
370 | /* Clear all of the status flags. | ||
371 | */ | ||
372 | bdp->cbd_sc &= ~BD_ENET_TX_STATS; | ||
373 | |||
374 | /* Set buffer length and buffer pointer. | ||
375 | */ | ||
376 | bdp->cbd_bufaddr = __pa(skb->data); | ||
377 | bdp->cbd_datlen = skb->len; | ||
378 | |||
379 | /* Save skb pointer. | ||
380 | */ | ||
381 | fep->tx_skbuff[fep->skb_cur] = skb; | ||
382 | |||
383 | fep->stats.tx_bytes += skb->len; | ||
384 | fep->skb_cur = (fep->skb_cur+1) & TX_RING_MOD_MASK; | ||
385 | |||
386 | /* Push the data cache so the CPM does not get stale memory | ||
387 | * data. | ||
388 | */ | ||
389 | flush_dcache_range((unsigned long)skb->data, | ||
390 | (unsigned long)skb->data + skb->len); | ||
391 | |||
392 | /* disable interrupts while triggering transmit */ | ||
393 | spin_lock_irq(&fep->lock); | ||
394 | |||
395 | /* Send it on its way. Tell FEC its ready, interrupt when done, | ||
396 | * its the last BD of the frame, and to put the CRC on the end. | ||
397 | */ | ||
398 | |||
399 | bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR | ||
400 | | BD_ENET_TX_LAST | BD_ENET_TX_TC); | ||
401 | |||
402 | dev->trans_start = jiffies; | ||
403 | |||
404 | /* Trigger transmission start */ | ||
405 | fecp->fec_x_des_active = 0x01000000; | ||
406 | |||
407 | /* If this was the last BD in the ring, start at the beginning again. | ||
408 | */ | ||
409 | if (bdp->cbd_sc & BD_ENET_TX_WRAP) { | ||
410 | bdp = fep->tx_bd_base; | ||
411 | } else { | ||
412 | bdp++; | ||
413 | } | ||
414 | |||
415 | if (bdp->cbd_sc & BD_ENET_TX_READY) { | ||
416 | netif_stop_queue(dev); | ||
417 | fep->tx_full = 1; | ||
418 | } | ||
419 | |||
420 | fep->cur_tx = (cbd_t *)bdp; | ||
421 | |||
422 | spin_unlock_irq(&fep->lock); | ||
423 | |||
424 | return 0; | ||
425 | } | ||
426 | |||
427 | static void | ||
428 | fec_timeout(struct net_device *dev) | ||
429 | { | ||
430 | struct fec_enet_private *fep = dev->priv; | ||
431 | |||
432 | printk("%s: transmit timed out.\n", dev->name); | ||
433 | fep->stats.tx_errors++; | ||
434 | #ifndef final_version | ||
435 | { | ||
436 | int i; | ||
437 | cbd_t *bdp; | ||
438 | |||
439 | printk("Ring data dump: cur_tx %lx%s, dirty_tx %lx cur_rx: %lx\n", | ||
440 | (unsigned long)fep->cur_tx, fep->tx_full ? " (full)" : "", | ||
441 | (unsigned long)fep->dirty_tx, | ||
442 | (unsigned long)fep->cur_rx); | ||
443 | |||
444 | bdp = fep->tx_bd_base; | ||
445 | printk(" tx: %u buffers\n", TX_RING_SIZE); | ||
446 | for (i = 0 ; i < TX_RING_SIZE; i++) { | ||
447 | printk(" %08x: %04x %04x %08x\n", | ||
448 | (uint) bdp, | ||
449 | bdp->cbd_sc, | ||
450 | bdp->cbd_datlen, | ||
451 | bdp->cbd_bufaddr); | ||
452 | bdp++; | ||
453 | } | ||
454 | |||
455 | bdp = fep->rx_bd_base; | ||
456 | printk(" rx: %lu buffers\n", RX_RING_SIZE); | ||
457 | for (i = 0 ; i < RX_RING_SIZE; i++) { | ||
458 | printk(" %08x: %04x %04x %08x\n", | ||
459 | (uint) bdp, | ||
460 | bdp->cbd_sc, | ||
461 | bdp->cbd_datlen, | ||
462 | bdp->cbd_bufaddr); | ||
463 | bdp++; | ||
464 | } | ||
465 | } | ||
466 | #endif | ||
467 | if (!fep->tx_full) | ||
468 | netif_wake_queue(dev); | ||
469 | } | ||
470 | |||
471 | /* The interrupt handler. | ||
472 | * This is called from the MPC core interrupt. | ||
473 | */ | ||
474 | static void | ||
475 | fec_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs) | ||
476 | { | ||
477 | struct net_device *dev = dev_id; | ||
478 | volatile fec_t *fecp; | ||
479 | uint int_events; | ||
480 | #ifdef CONFIG_FEC_PACKETHOOK | ||
481 | struct fec_enet_private *fep = dev->priv; | ||
482 | __u32 regval; | ||
483 | |||
484 | if (fep->ph_regaddr) regval = *fep->ph_regaddr; | ||
485 | #endif | ||
486 | fecp = (volatile fec_t*)dev->base_addr; | ||
487 | |||
488 | /* Get the interrupt events that caused us to be here. | ||
489 | */ | ||
490 | while ((int_events = fecp->fec_ievent) != 0) { | ||
491 | fecp->fec_ievent = int_events; | ||
492 | if ((int_events & (FEC_ENET_HBERR | FEC_ENET_BABR | | ||
493 | FEC_ENET_BABT | FEC_ENET_EBERR)) != 0) { | ||
494 | printk("FEC ERROR %x\n", int_events); | ||
495 | } | ||
496 | |||
497 | /* Handle receive event in its own function. | ||
498 | */ | ||
499 | if (int_events & FEC_ENET_RXF) { | ||
500 | #ifdef CONFIG_FEC_PACKETHOOK | ||
501 | fec_enet_rx(dev, regval); | ||
502 | #else | ||
503 | fec_enet_rx(dev); | ||
504 | #endif | ||
505 | } | ||
506 | |||
507 | /* Transmit OK, or non-fatal error. Update the buffer | ||
508 | descriptors. FEC handles all errors, we just discover | ||
509 | them as part of the transmit process. | ||
510 | */ | ||
511 | if (int_events & FEC_ENET_TXF) { | ||
512 | #ifdef CONFIG_FEC_PACKETHOOK | ||
513 | fec_enet_tx(dev, regval); | ||
514 | #else | ||
515 | fec_enet_tx(dev); | ||
516 | #endif | ||
517 | } | ||
518 | |||
519 | if (int_events & FEC_ENET_MII) { | ||
520 | #ifdef CONFIG_USE_MDIO | ||
521 | fec_enet_mii(dev); | ||
522 | #else | ||
523 | printk("%s[%d] %s: unexpected FEC_ENET_MII event\n", __FILE__,__LINE__,__FUNCTION__); | ||
524 | #endif /* CONFIG_USE_MDIO */ | ||
525 | } | ||
526 | |||
527 | } | ||
528 | } | ||
529 | |||
530 | |||
531 | static void | ||
532 | #ifdef CONFIG_FEC_PACKETHOOK | ||
533 | fec_enet_tx(struct net_device *dev, __u32 regval) | ||
534 | #else | ||
535 | fec_enet_tx(struct net_device *dev) | ||
536 | #endif | ||
537 | { | ||
538 | struct fec_enet_private *fep; | ||
539 | volatile cbd_t *bdp; | ||
540 | struct sk_buff *skb; | ||
541 | |||
542 | fep = dev->priv; | ||
543 | /* lock while transmitting */ | ||
544 | spin_lock(&fep->lock); | ||
545 | bdp = fep->dirty_tx; | ||
546 | |||
547 | while ((bdp->cbd_sc&BD_ENET_TX_READY) == 0) { | ||
548 | if (bdp == fep->cur_tx && fep->tx_full == 0) break; | ||
549 | |||
550 | skb = fep->tx_skbuff[fep->skb_dirty]; | ||
551 | /* Check for errors. */ | ||
552 | if (bdp->cbd_sc & (BD_ENET_TX_HB | BD_ENET_TX_LC | | ||
553 | BD_ENET_TX_RL | BD_ENET_TX_UN | | ||
554 | BD_ENET_TX_CSL)) { | ||
555 | fep->stats.tx_errors++; | ||
556 | if (bdp->cbd_sc & BD_ENET_TX_HB) /* No heartbeat */ | ||
557 | fep->stats.tx_heartbeat_errors++; | ||
558 | if (bdp->cbd_sc & BD_ENET_TX_LC) /* Late collision */ | ||
559 | fep->stats.tx_window_errors++; | ||
560 | if (bdp->cbd_sc & BD_ENET_TX_RL) /* Retrans limit */ | ||
561 | fep->stats.tx_aborted_errors++; | ||
562 | if (bdp->cbd_sc & BD_ENET_TX_UN) /* Underrun */ | ||
563 | fep->stats.tx_fifo_errors++; | ||
564 | if (bdp->cbd_sc & BD_ENET_TX_CSL) /* Carrier lost */ | ||
565 | fep->stats.tx_carrier_errors++; | ||
566 | } else { | ||
567 | #ifdef CONFIG_FEC_PACKETHOOK | ||
568 | /* Packet hook ... */ | ||
569 | if (fep->ph_txhandler && | ||
570 | ((struct ethhdr *)skb->data)->h_proto | ||
571 | == fep->ph_proto) { | ||
572 | fep->ph_txhandler((__u8*)skb->data, skb->len, | ||
573 | regval, fep->ph_priv); | ||
574 | } | ||
575 | #endif | ||
576 | fep->stats.tx_packets++; | ||
577 | } | ||
578 | |||
579 | #ifndef final_version | ||
580 | if (bdp->cbd_sc & BD_ENET_TX_READY) | ||
581 | printk("HEY! Enet xmit interrupt and TX_READY.\n"); | ||
582 | #endif | ||
583 | /* Deferred means some collisions occurred during transmit, | ||
584 | * but we eventually sent the packet OK. | ||
585 | */ | ||
586 | if (bdp->cbd_sc & BD_ENET_TX_DEF) | ||
587 | fep->stats.collisions++; | ||
588 | |||
589 | /* Free the sk buffer associated with this last transmit. | ||
590 | */ | ||
591 | #if 0 | ||
592 | printk("TXI: %x %x %x\n", bdp, skb, fep->skb_dirty); | ||
593 | #endif | ||
594 | dev_kfree_skb_irq (skb/*, FREE_WRITE*/); | ||
595 | fep->tx_skbuff[fep->skb_dirty] = NULL; | ||
596 | fep->skb_dirty = (fep->skb_dirty + 1) & TX_RING_MOD_MASK; | ||
597 | |||
598 | /* Update pointer to next buffer descriptor to be transmitted. | ||
599 | */ | ||
600 | if (bdp->cbd_sc & BD_ENET_TX_WRAP) | ||
601 | bdp = fep->tx_bd_base; | ||
602 | else | ||
603 | bdp++; | ||
604 | |||
605 | /* Since we have freed up a buffer, the ring is no longer | ||
606 | * full. | ||
607 | */ | ||
608 | if (fep->tx_full) { | ||
609 | fep->tx_full = 0; | ||
610 | if (netif_queue_stopped(dev)) | ||
611 | netif_wake_queue(dev); | ||
612 | } | ||
613 | #ifdef CONFIG_FEC_PACKETHOOK | ||
614 | /* Re-read register. Not exactly guaranteed to be correct, | ||
615 | but... */ | ||
616 | if (fep->ph_regaddr) regval = *fep->ph_regaddr; | ||
617 | #endif | ||
618 | } | ||
619 | fep->dirty_tx = (cbd_t *)bdp; | ||
620 | spin_unlock(&fep->lock); | ||
621 | } | ||
622 | |||
623 | |||
624 | /* During a receive, the cur_rx points to the current incoming buffer. | ||
625 | * When we update through the ring, if the next incoming buffer has | ||
626 | * not been given to the system, we just set the empty indicator, | ||
627 | * effectively tossing the packet. | ||
628 | */ | ||
629 | static void | ||
630 | #ifdef CONFIG_FEC_PACKETHOOK | ||
631 | fec_enet_rx(struct net_device *dev, __u32 regval) | ||
632 | #else | ||
633 | fec_enet_rx(struct net_device *dev) | ||
634 | #endif | ||
635 | { | ||
636 | struct fec_enet_private *fep; | ||
637 | volatile fec_t *fecp; | ||
638 | volatile cbd_t *bdp; | ||
639 | struct sk_buff *skb; | ||
640 | ushort pkt_len; | ||
641 | __u8 *data; | ||
642 | |||
643 | fep = dev->priv; | ||
644 | fecp = (volatile fec_t*)dev->base_addr; | ||
645 | |||
646 | /* First, grab all of the stats for the incoming packet. | ||
647 | * These get messed up if we get called due to a busy condition. | ||
648 | */ | ||
649 | bdp = fep->cur_rx; | ||
650 | |||
651 | while (!(bdp->cbd_sc & BD_ENET_RX_EMPTY)) { | ||
652 | |||
653 | #ifndef final_version | ||
654 | /* Since we have allocated space to hold a complete frame, | ||
655 | * the last indicator should be set. | ||
656 | */ | ||
657 | if ((bdp->cbd_sc & BD_ENET_RX_LAST) == 0) | ||
658 | printk("FEC ENET: rcv is not +last\n"); | ||
659 | #endif | ||
660 | |||
661 | /* Check for errors. */ | ||
662 | if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | | ||
663 | BD_ENET_RX_CR | BD_ENET_RX_OV)) { | ||
664 | fep->stats.rx_errors++; | ||
665 | if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH)) { | ||
666 | /* Frame too long or too short. */ | ||
667 | fep->stats.rx_length_errors++; | ||
668 | } | ||
669 | if (bdp->cbd_sc & BD_ENET_RX_NO) /* Frame alignment */ | ||
670 | fep->stats.rx_frame_errors++; | ||
671 | if (bdp->cbd_sc & BD_ENET_RX_CR) /* CRC Error */ | ||
672 | fep->stats.rx_crc_errors++; | ||
673 | if (bdp->cbd_sc & BD_ENET_RX_OV) /* FIFO overrun */ | ||
674 | fep->stats.rx_crc_errors++; | ||
675 | } | ||
676 | |||
677 | /* Report late collisions as a frame error. | ||
678 | * On this error, the BD is closed, but we don't know what we | ||
679 | * have in the buffer. So, just drop this frame on the floor. | ||
680 | */ | ||
681 | if (bdp->cbd_sc & BD_ENET_RX_CL) { | ||
682 | fep->stats.rx_errors++; | ||
683 | fep->stats.rx_frame_errors++; | ||
684 | goto rx_processing_done; | ||
685 | } | ||
686 | |||
687 | /* Process the incoming frame. | ||
688 | */ | ||
689 | fep->stats.rx_packets++; | ||
690 | pkt_len = bdp->cbd_datlen; | ||
691 | fep->stats.rx_bytes += pkt_len; | ||
692 | data = fep->rx_vaddr[bdp - fep->rx_bd_base]; | ||
693 | |||
694 | #ifdef CONFIG_FEC_PACKETHOOK | ||
695 | /* Packet hook ... */ | ||
696 | if (fep->ph_rxhandler) { | ||
697 | if (((struct ethhdr *)data)->h_proto == fep->ph_proto) { | ||
698 | switch (fep->ph_rxhandler(data, pkt_len, regval, | ||
699 | fep->ph_priv)) { | ||
700 | case 1: | ||
701 | goto rx_processing_done; | ||
702 | break; | ||
703 | case 0: | ||
704 | break; | ||
705 | default: | ||
706 | fep->stats.rx_errors++; | ||
707 | goto rx_processing_done; | ||
708 | } | ||
709 | } | ||
710 | } | ||
711 | |||
712 | /* If it wasn't filtered - copy it to an sk buffer. */ | ||
713 | #endif | ||
714 | |||
715 | /* This does 16 byte alignment, exactly what we need. | ||
716 | * The packet length includes FCS, but we don't want to | ||
717 | * include that when passing upstream as it messes up | ||
718 | * bridging applications. | ||
719 | */ | ||
720 | skb = dev_alloc_skb(pkt_len-4); | ||
721 | |||
722 | if (skb == NULL) { | ||
723 | printk("%s: Memory squeeze, dropping packet.\n", dev->name); | ||
724 | fep->stats.rx_dropped++; | ||
725 | } else { | ||
726 | skb->dev = dev; | ||
727 | skb_put(skb,pkt_len-4); /* Make room */ | ||
728 | eth_copy_and_sum(skb, data, pkt_len-4, 0); | ||
729 | skb->protocol=eth_type_trans(skb,dev); | ||
730 | netif_rx(skb); | ||
731 | } | ||
732 | rx_processing_done: | ||
733 | |||
734 | /* Clear the status flags for this buffer. | ||
735 | */ | ||
736 | bdp->cbd_sc &= ~BD_ENET_RX_STATS; | ||
737 | |||
738 | /* Mark the buffer empty. | ||
739 | */ | ||
740 | bdp->cbd_sc |= BD_ENET_RX_EMPTY; | ||
741 | |||
742 | /* Update BD pointer to next entry. | ||
743 | */ | ||
744 | if (bdp->cbd_sc & BD_ENET_RX_WRAP) | ||
745 | bdp = fep->rx_bd_base; | ||
746 | else | ||
747 | bdp++; | ||
748 | |||
749 | #if 1 | ||
750 | /* Doing this here will keep the FEC running while we process | ||
751 | * incoming frames. On a heavily loaded network, we should be | ||
752 | * able to keep up at the expense of system resources. | ||
753 | */ | ||
754 | fecp->fec_r_des_active = 0x01000000; | ||
755 | #endif | ||
756 | #ifdef CONFIG_FEC_PACKETHOOK | ||
757 | /* Re-read register. Not exactly guaranteed to be correct, | ||
758 | but... */ | ||
759 | if (fep->ph_regaddr) regval = *fep->ph_regaddr; | ||
760 | #endif | ||
761 | } /* while (!(bdp->cbd_sc & BD_ENET_RX_EMPTY)) */ | ||
762 | fep->cur_rx = (cbd_t *)bdp; | ||
763 | |||
764 | #if 0 | ||
765 | /* Doing this here will allow us to process all frames in the | ||
766 | * ring before the FEC is allowed to put more there. On a heavily | ||
767 | * loaded network, some frames may be lost. Unfortunately, this | ||
768 | * increases the interrupt overhead since we can potentially work | ||
769 | * our way back to the interrupt return only to come right back | ||
770 | * here. | ||
771 | */ | ||
772 | fecp->fec_r_des_active = 0x01000000; | ||
773 | #endif | ||
774 | } | ||
775 | |||
776 | |||
777 | #ifdef CONFIG_USE_MDIO | ||
778 | static void | ||
779 | fec_enet_mii(struct net_device *dev) | ||
780 | { | ||
781 | struct fec_enet_private *fep; | ||
782 | volatile fec_t *ep; | ||
783 | mii_list_t *mip; | ||
784 | uint mii_reg; | ||
785 | |||
786 | fep = (struct fec_enet_private *)dev->priv; | ||
787 | ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec); | ||
788 | mii_reg = ep->fec_mii_data; | ||
789 | |||
790 | if ((mip = mii_head) == NULL) { | ||
791 | printk("MII and no head!\n"); | ||
792 | return; | ||
793 | } | ||
794 | |||
795 | if (mip->mii_func != NULL) | ||
796 | (*(mip->mii_func))(mii_reg, dev); | ||
797 | |||
798 | mii_head = mip->mii_next; | ||
799 | mip->mii_next = mii_free; | ||
800 | mii_free = mip; | ||
801 | |||
802 | if ((mip = mii_head) != NULL) { | ||
803 | ep->fec_mii_data = mip->mii_regval; | ||
804 | |||
805 | } | ||
806 | } | ||
807 | |||
808 | static int | ||
809 | mii_queue(struct net_device *dev, int regval, void (*func)(uint, struct net_device *)) | ||
810 | { | ||
811 | struct fec_enet_private *fep; | ||
812 | unsigned long flags; | ||
813 | mii_list_t *mip; | ||
814 | int retval; | ||
815 | |||
816 | /* Add PHY address to register command. | ||
817 | */ | ||
818 | fep = dev->priv; | ||
819 | regval |= fep->phy_addr << 23; | ||
820 | |||
821 | retval = 0; | ||
822 | |||
823 | /* lock while modifying mii_list */ | ||
824 | spin_lock_irqsave(&fep->lock, flags); | ||
825 | |||
826 | if ((mip = mii_free) != NULL) { | ||
827 | mii_free = mip->mii_next; | ||
828 | mip->mii_regval = regval; | ||
829 | mip->mii_func = func; | ||
830 | mip->mii_next = NULL; | ||
831 | if (mii_head) { | ||
832 | mii_tail->mii_next = mip; | ||
833 | mii_tail = mip; | ||
834 | } else { | ||
835 | mii_head = mii_tail = mip; | ||
836 | (&(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec))->fec_mii_data = regval; | ||
837 | } | ||
838 | } else { | ||
839 | retval = 1; | ||
840 | } | ||
841 | |||
842 | spin_unlock_irqrestore(&fep->lock, flags); | ||
843 | |||
844 | return(retval); | ||
845 | } | ||
846 | |||
847 | static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c) | ||
848 | { | ||
849 | int k; | ||
850 | |||
851 | if(!c) | ||
852 | return; | ||
853 | |||
854 | for(k = 0; (c+k)->mii_data != mk_mii_end; k++) | ||
855 | mii_queue(dev, (c+k)->mii_data, (c+k)->funct); | ||
856 | } | ||
857 | |||
858 | static void mii_parse_sr(uint mii_reg, struct net_device *dev) | ||
859 | { | ||
860 | struct fec_enet_private *fep = dev->priv; | ||
861 | volatile uint *s = &(fep->phy_status); | ||
862 | |||
863 | *s &= ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC); | ||
864 | |||
865 | if (mii_reg & 0x0004) | ||
866 | *s |= PHY_STAT_LINK; | ||
867 | if (mii_reg & 0x0010) | ||
868 | *s |= PHY_STAT_FAULT; | ||
869 | if (mii_reg & 0x0020) | ||
870 | *s |= PHY_STAT_ANC; | ||
871 | |||
872 | fep->link = (*s & PHY_STAT_LINK) ? 1 : 0; | ||
873 | } | ||
874 | |||
875 | static void mii_parse_cr(uint mii_reg, struct net_device *dev) | ||
876 | { | ||
877 | struct fec_enet_private *fep = dev->priv; | ||
878 | volatile uint *s = &(fep->phy_status); | ||
879 | |||
880 | *s &= ~(PHY_CONF_ANE | PHY_CONF_LOOP); | ||
881 | |||
882 | if (mii_reg & 0x1000) | ||
883 | *s |= PHY_CONF_ANE; | ||
884 | if (mii_reg & 0x4000) | ||
885 | *s |= PHY_CONF_LOOP; | ||
886 | } | ||
887 | |||
888 | static void mii_parse_anar(uint mii_reg, struct net_device *dev) | ||
889 | { | ||
890 | struct fec_enet_private *fep = dev->priv; | ||
891 | volatile uint *s = &(fep->phy_status); | ||
892 | |||
893 | *s &= ~(PHY_CONF_SPMASK); | ||
894 | |||
895 | if (mii_reg & 0x0020) | ||
896 | *s |= PHY_CONF_10HDX; | ||
897 | if (mii_reg & 0x0040) | ||
898 | *s |= PHY_CONF_10FDX; | ||
899 | if (mii_reg & 0x0080) | ||
900 | *s |= PHY_CONF_100HDX; | ||
901 | if (mii_reg & 0x00100) | ||
902 | *s |= PHY_CONF_100FDX; | ||
903 | } | ||
904 | #if 0 | ||
905 | static void mii_disp_reg(uint mii_reg, struct net_device *dev) | ||
906 | { | ||
907 | printk("reg %u = 0x%04x\n", (mii_reg >> 18) & 0x1f, mii_reg & 0xffff); | ||
908 | } | ||
909 | #endif | ||
910 | |||
911 | /* ------------------------------------------------------------------------- */ | ||
912 | /* The Level one LXT970 is used by many boards */ | ||
913 | |||
914 | #ifdef CONFIG_FEC_LXT970 | ||
915 | |||
916 | #define MII_LXT970_MIRROR 16 /* Mirror register */ | ||
917 | #define MII_LXT970_IER 17 /* Interrupt Enable Register */ | ||
918 | #define MII_LXT970_ISR 18 /* Interrupt Status Register */ | ||
919 | #define MII_LXT970_CONFIG 19 /* Configuration Register */ | ||
920 | #define MII_LXT970_CSR 20 /* Chip Status Register */ | ||
921 | |||
922 | static void mii_parse_lxt970_csr(uint mii_reg, struct net_device *dev) | ||
923 | { | ||
924 | struct fec_enet_private *fep = dev->priv; | ||
925 | volatile uint *s = &(fep->phy_status); | ||
926 | |||
927 | *s &= ~(PHY_STAT_SPMASK); | ||
928 | |||
929 | if (mii_reg & 0x0800) { | ||
930 | if (mii_reg & 0x1000) | ||
931 | *s |= PHY_STAT_100FDX; | ||
932 | else | ||
933 | *s |= PHY_STAT_100HDX; | ||
934 | } | ||
935 | else { | ||
936 | if (mii_reg & 0x1000) | ||
937 | *s |= PHY_STAT_10FDX; | ||
938 | else | ||
939 | *s |= PHY_STAT_10HDX; | ||
940 | } | ||
941 | } | ||
942 | |||
943 | static phy_info_t phy_info_lxt970 = { | ||
944 | 0x07810000, | ||
945 | "LXT970", | ||
946 | |||
947 | (const phy_cmd_t []) { /* config */ | ||
948 | #if 0 | ||
949 | // { mk_mii_write(MII_REG_ANAR, 0x0021), NULL }, | ||
950 | |||
951 | /* Set default operation of 100-TX....for some reason | ||
952 | * some of these bits are set on power up, which is wrong. | ||
953 | */ | ||
954 | { mk_mii_write(MII_LXT970_CONFIG, 0), NULL }, | ||
955 | #endif | ||
956 | { mk_mii_read(MII_REG_CR), mii_parse_cr }, | ||
957 | { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, | ||
958 | { mk_mii_end, } | ||
959 | }, | ||
960 | (const phy_cmd_t []) { /* startup - enable interrupts */ | ||
961 | { mk_mii_write(MII_LXT970_IER, 0x0002), NULL }, | ||
962 | { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ | ||
963 | { mk_mii_end, } | ||
964 | }, | ||
965 | (const phy_cmd_t []) { /* ack_int */ | ||
966 | /* read SR and ISR to acknowledge */ | ||
967 | |||
968 | { mk_mii_read(MII_REG_SR), mii_parse_sr }, | ||
969 | { mk_mii_read(MII_LXT970_ISR), NULL }, | ||
970 | |||
971 | /* find out the current status */ | ||
972 | |||
973 | { mk_mii_read(MII_LXT970_CSR), mii_parse_lxt970_csr }, | ||
974 | { mk_mii_end, } | ||
975 | }, | ||
976 | (const phy_cmd_t []) { /* shutdown - disable interrupts */ | ||
977 | { mk_mii_write(MII_LXT970_IER, 0x0000), NULL }, | ||
978 | { mk_mii_end, } | ||
979 | }, | ||
980 | }; | ||
981 | |||
982 | #endif /* CONFIG_FEC_LXT970 */ | ||
983 | |||
984 | /* ------------------------------------------------------------------------- */ | ||
985 | /* The Level one LXT971 is used on some of my custom boards */ | ||
986 | |||
987 | #ifdef CONFIG_FEC_LXT971 | ||
988 | |||
989 | /* register definitions for the 971 */ | ||
990 | |||
991 | #define MII_LXT971_PCR 16 /* Port Control Register */ | ||
992 | #define MII_LXT971_SR2 17 /* Status Register 2 */ | ||
993 | #define MII_LXT971_IER 18 /* Interrupt Enable Register */ | ||
994 | #define MII_LXT971_ISR 19 /* Interrupt Status Register */ | ||
995 | #define MII_LXT971_LCR 20 /* LED Control Register */ | ||
996 | #define MII_LXT971_TCR 30 /* Transmit Control Register */ | ||
997 | |||
998 | /* | ||
999 | * I had some nice ideas of running the MDIO faster... | ||
1000 | * The 971 should support 8MHz and I tried it, but things acted really | ||
1001 | * weird, so 2.5 MHz ought to be enough for anyone... | ||
1002 | */ | ||
1003 | |||
1004 | static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev) | ||
1005 | { | ||
1006 | struct fec_enet_private *fep = dev->priv; | ||
1007 | volatile uint *s = &(fep->phy_status); | ||
1008 | |||
1009 | *s &= ~(PHY_STAT_SPMASK); | ||
1010 | |||
1011 | if (mii_reg & 0x4000) { | ||
1012 | if (mii_reg & 0x0200) | ||
1013 | *s |= PHY_STAT_100FDX; | ||
1014 | else | ||
1015 | *s |= PHY_STAT_100HDX; | ||
1016 | } | ||
1017 | else { | ||
1018 | if (mii_reg & 0x0200) | ||
1019 | *s |= PHY_STAT_10FDX; | ||
1020 | else | ||
1021 | *s |= PHY_STAT_10HDX; | ||
1022 | } | ||
1023 | if (mii_reg & 0x0008) | ||
1024 | *s |= PHY_STAT_FAULT; | ||
1025 | } | ||
1026 | |||
1027 | static phy_info_t phy_info_lxt971 = { | ||
1028 | 0x0001378e, | ||
1029 | "LXT971", | ||
1030 | |||
1031 | (const phy_cmd_t []) { /* config */ | ||
1032 | // { mk_mii_write(MII_REG_ANAR, 0x021), NULL }, /* 10 Mbps, HD */ | ||
1033 | { mk_mii_read(MII_REG_CR), mii_parse_cr }, | ||
1034 | { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, | ||
1035 | { mk_mii_end, } | ||
1036 | }, | ||
1037 | (const phy_cmd_t []) { /* startup - enable interrupts */ | ||
1038 | { mk_mii_write(MII_LXT971_IER, 0x00f2), NULL }, | ||
1039 | { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ | ||
1040 | |||
1041 | /* Somehow does the 971 tell me that the link is down | ||
1042 | * the first read after power-up. | ||
1043 | * read here to get a valid value in ack_int */ | ||
1044 | |||
1045 | { mk_mii_read(MII_REG_SR), mii_parse_sr }, | ||
1046 | { mk_mii_end, } | ||
1047 | }, | ||
1048 | (const phy_cmd_t []) { /* ack_int */ | ||
1049 | /* find out the current status */ | ||
1050 | |||
1051 | { mk_mii_read(MII_REG_SR), mii_parse_sr }, | ||
1052 | { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 }, | ||
1053 | |||
1054 | /* we only need to read ISR to acknowledge */ | ||
1055 | |||
1056 | { mk_mii_read(MII_LXT971_ISR), NULL }, | ||
1057 | { mk_mii_end, } | ||
1058 | }, | ||
1059 | (const phy_cmd_t []) { /* shutdown - disable interrupts */ | ||
1060 | { mk_mii_write(MII_LXT971_IER, 0x0000), NULL }, | ||
1061 | { mk_mii_end, } | ||
1062 | }, | ||
1063 | }; | ||
1064 | |||
1065 | #endif /* CONFIG_FEC_LXT970 */ | ||
1066 | |||
1067 | |||
1068 | /* ------------------------------------------------------------------------- */ | ||
1069 | /* The Quality Semiconductor QS6612 is used on the RPX CLLF */ | ||
1070 | |||
1071 | #ifdef CONFIG_FEC_QS6612 | ||
1072 | |||
1073 | /* register definitions */ | ||
1074 | |||
1075 | #define MII_QS6612_MCR 17 /* Mode Control Register */ | ||
1076 | #define MII_QS6612_FTR 27 /* Factory Test Register */ | ||
1077 | #define MII_QS6612_MCO 28 /* Misc. Control Register */ | ||
1078 | #define MII_QS6612_ISR 29 /* Interrupt Source Register */ | ||
1079 | #define MII_QS6612_IMR 30 /* Interrupt Mask Register */ | ||
1080 | #define MII_QS6612_PCR 31 /* 100BaseTx PHY Control Reg. */ | ||
1081 | |||
1082 | static void mii_parse_qs6612_pcr(uint mii_reg, struct net_device *dev) | ||
1083 | { | ||
1084 | struct fec_enet_private *fep = dev->priv; | ||
1085 | volatile uint *s = &(fep->phy_status); | ||
1086 | |||
1087 | *s &= ~(PHY_STAT_SPMASK); | ||
1088 | |||
1089 | switch((mii_reg >> 2) & 7) { | ||
1090 | case 1: *s |= PHY_STAT_10HDX; break; | ||
1091 | case 2: *s |= PHY_STAT_100HDX; break; | ||
1092 | case 5: *s |= PHY_STAT_10FDX; break; | ||
1093 | case 6: *s |= PHY_STAT_100FDX; break; | ||
1094 | } | ||
1095 | } | ||
1096 | |||
1097 | static phy_info_t phy_info_qs6612 = { | ||
1098 | 0x00181440, | ||
1099 | "QS6612", | ||
1100 | |||
1101 | (const phy_cmd_t []) { /* config */ | ||
1102 | // { mk_mii_write(MII_REG_ANAR, 0x061), NULL }, /* 10 Mbps */ | ||
1103 | |||
1104 | /* The PHY powers up isolated on the RPX, | ||
1105 | * so send a command to allow operation. | ||
1106 | */ | ||
1107 | |||
1108 | { mk_mii_write(MII_QS6612_PCR, 0x0dc0), NULL }, | ||
1109 | |||
1110 | /* parse cr and anar to get some info */ | ||
1111 | |||
1112 | { mk_mii_read(MII_REG_CR), mii_parse_cr }, | ||
1113 | { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, | ||
1114 | { mk_mii_end, } | ||
1115 | }, | ||
1116 | (const phy_cmd_t []) { /* startup - enable interrupts */ | ||
1117 | { mk_mii_write(MII_QS6612_IMR, 0x003a), NULL }, | ||
1118 | { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ | ||
1119 | { mk_mii_end, } | ||
1120 | }, | ||
1121 | (const phy_cmd_t []) { /* ack_int */ | ||
1122 | |||
1123 | /* we need to read ISR, SR and ANER to acknowledge */ | ||
1124 | |||
1125 | { mk_mii_read(MII_QS6612_ISR), NULL }, | ||
1126 | { mk_mii_read(MII_REG_SR), mii_parse_sr }, | ||
1127 | { mk_mii_read(MII_REG_ANER), NULL }, | ||
1128 | |||
1129 | /* read pcr to get info */ | ||
1130 | |||
1131 | { mk_mii_read(MII_QS6612_PCR), mii_parse_qs6612_pcr }, | ||
1132 | { mk_mii_end, } | ||
1133 | }, | ||
1134 | (const phy_cmd_t []) { /* shutdown - disable interrupts */ | ||
1135 | { mk_mii_write(MII_QS6612_IMR, 0x0000), NULL }, | ||
1136 | { mk_mii_end, } | ||
1137 | }, | ||
1138 | }; | ||
1139 | |||
1140 | #endif /* CONFIG_FEC_QS6612 */ | ||
1141 | |||
1142 | /* ------------------------------------------------------------------------- */ | ||
1143 | /* The Advanced Micro Devices AM79C874 is used on the ICU862 */ | ||
1144 | |||
1145 | #ifdef CONFIG_FEC_AM79C874 | ||
1146 | |||
1147 | /* register definitions for the 79C874 */ | ||
1148 | |||
1149 | #define MII_AM79C874_MFR 16 /* Miscellaneous Features Register */ | ||
1150 | #define MII_AM79C874_ICSR 17 /* Interrupt Control/Status Register */ | ||
1151 | #define MII_AM79C874_DR 18 /* Diagnostic Register */ | ||
1152 | #define MII_AM79C874_PMLR 19 /* Power Management & Loopback Register */ | ||
1153 | #define MII_AM79C874_MCR 21 /* Mode Control Register */ | ||
1154 | #define MII_AM79C874_DC 23 /* Disconnect Counter */ | ||
1155 | #define MII_AM79C874_REC 24 /* Receiver Error Counter */ | ||
1156 | |||
1157 | static void mii_parse_amd79c874_dr(uint mii_reg, struct net_device *dev, uint data) | ||
1158 | { | ||
1159 | volatile struct fec_enet_private *fep = dev->priv; | ||
1160 | uint s = fep->phy_status; | ||
1161 | |||
1162 | s &= ~(PHY_STAT_SPMASK); | ||
1163 | |||
1164 | /* Register 18: Bit 10 is data rate, 11 is Duplex */ | ||
1165 | switch ((mii_reg >> 10) & 3) { | ||
1166 | case 0: s |= PHY_STAT_10HDX; break; | ||
1167 | case 1: s |= PHY_STAT_100HDX; break; | ||
1168 | case 2: s |= PHY_STAT_10FDX; break; | ||
1169 | case 3: s |= PHY_STAT_100FDX; break; | ||
1170 | } | ||
1171 | |||
1172 | fep->phy_status = s; | ||
1173 | } | ||
1174 | |||
1175 | static phy_info_t phy_info_amd79c874 = { | ||
1176 | 0x00022561, | ||
1177 | "AM79C874", | ||
1178 | |||
1179 | (const phy_cmd_t []) { /* config */ | ||
1180 | // { mk_mii_write(MII_REG_ANAR, 0x021), NULL }, /* 10 Mbps, HD */ | ||
1181 | { mk_mii_read(MII_REG_CR), mii_parse_cr }, | ||
1182 | { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, | ||
1183 | { mk_mii_end, } | ||
1184 | }, | ||
1185 | (const phy_cmd_t []) { /* startup - enable interrupts */ | ||
1186 | { mk_mii_write(MII_AM79C874_ICSR, 0xff00), NULL }, | ||
1187 | { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ | ||
1188 | { mk_mii_end, } | ||
1189 | }, | ||
1190 | (const phy_cmd_t []) { /* ack_int */ | ||
1191 | /* find out the current status */ | ||
1192 | |||
1193 | { mk_mii_read(MII_REG_SR), mii_parse_sr }, | ||
1194 | { mk_mii_read(MII_AM79C874_DR), mii_parse_amd79c874_dr }, | ||
1195 | |||
1196 | /* we only need to read ICSR to acknowledge */ | ||
1197 | |||
1198 | { mk_mii_read(MII_AM79C874_ICSR), NULL }, | ||
1199 | { mk_mii_end, } | ||
1200 | }, | ||
1201 | (const phy_cmd_t []) { /* shutdown - disable interrupts */ | ||
1202 | { mk_mii_write(MII_AM79C874_ICSR, 0x0000), NULL }, | ||
1203 | { mk_mii_end, } | ||
1204 | }, | ||
1205 | }; | ||
1206 | |||
1207 | #endif /* CONFIG_FEC_AM79C874 */ | ||
1208 | |||
1209 | static phy_info_t *phy_info[] = { | ||
1210 | |||
1211 | #ifdef CONFIG_FEC_LXT970 | ||
1212 | &phy_info_lxt970, | ||
1213 | #endif /* CONFIG_FEC_LXT970 */ | ||
1214 | |||
1215 | #ifdef CONFIG_FEC_LXT971 | ||
1216 | &phy_info_lxt971, | ||
1217 | #endif /* CONFIG_FEC_LXT971 */ | ||
1218 | |||
1219 | #ifdef CONFIG_FEC_QS6612 | ||
1220 | &phy_info_qs6612, | ||
1221 | #endif /* CONFIG_FEC_QS6612 */ | ||
1222 | |||
1223 | #ifdef CONFIG_FEC_AM79C874 | ||
1224 | &phy_info_amd79c874, | ||
1225 | #endif /* CONFIG_FEC_AM79C874 */ | ||
1226 | |||
1227 | NULL | ||
1228 | }; | ||
1229 | |||
1230 | static void mii_display_status(struct net_device *dev) | ||
1231 | { | ||
1232 | struct fec_enet_private *fep = dev->priv; | ||
1233 | volatile uint *s = &(fep->phy_status); | ||
1234 | |||
1235 | if (!fep->link && !fep->old_link) { | ||
1236 | /* Link is still down - don't print anything */ | ||
1237 | return; | ||
1238 | } | ||
1239 | |||
1240 | printk("%s: status: ", dev->name); | ||
1241 | |||
1242 | if (!fep->link) { | ||
1243 | printk("link down"); | ||
1244 | } else { | ||
1245 | printk("link up"); | ||
1246 | |||
1247 | switch(*s & PHY_STAT_SPMASK) { | ||
1248 | case PHY_STAT_100FDX: printk(", 100 Mbps Full Duplex"); break; | ||
1249 | case PHY_STAT_100HDX: printk(", 100 Mbps Half Duplex"); break; | ||
1250 | case PHY_STAT_10FDX: printk(", 10 Mbps Full Duplex"); break; | ||
1251 | case PHY_STAT_10HDX: printk(", 10 Mbps Half Duplex"); break; | ||
1252 | default: | ||
1253 | printk(", Unknown speed/duplex"); | ||
1254 | } | ||
1255 | |||
1256 | if (*s & PHY_STAT_ANC) | ||
1257 | printk(", auto-negotiation complete"); | ||
1258 | } | ||
1259 | |||
1260 | if (*s & PHY_STAT_FAULT) | ||
1261 | printk(", remote fault"); | ||
1262 | |||
1263 | printk(".\n"); | ||
1264 | } | ||
1265 | |||
1266 | static void mii_display_config(struct net_device *dev) | ||
1267 | { | ||
1268 | struct fec_enet_private *fep = dev->priv; | ||
1269 | volatile uint *s = &(fep->phy_status); | ||
1270 | |||
1271 | printk("%s: config: auto-negotiation ", dev->name); | ||
1272 | |||
1273 | if (*s & PHY_CONF_ANE) | ||
1274 | printk("on"); | ||
1275 | else | ||
1276 | printk("off"); | ||
1277 | |||
1278 | if (*s & PHY_CONF_100FDX) | ||
1279 | printk(", 100FDX"); | ||
1280 | if (*s & PHY_CONF_100HDX) | ||
1281 | printk(", 100HDX"); | ||
1282 | if (*s & PHY_CONF_10FDX) | ||
1283 | printk(", 10FDX"); | ||
1284 | if (*s & PHY_CONF_10HDX) | ||
1285 | printk(", 10HDX"); | ||
1286 | if (!(*s & PHY_CONF_SPMASK)) | ||
1287 | printk(", No speed/duplex selected?"); | ||
1288 | |||
1289 | if (*s & PHY_CONF_LOOP) | ||
1290 | printk(", loopback enabled"); | ||
1291 | |||
1292 | printk(".\n"); | ||
1293 | |||
1294 | fep->sequence_done = 1; | ||
1295 | } | ||
1296 | |||
1297 | static void mii_relink(struct net_device *dev) | ||
1298 | { | ||
1299 | struct fec_enet_private *fep = dev->priv; | ||
1300 | int duplex; | ||
1301 | |||
1302 | fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0; | ||
1303 | mii_display_status(dev); | ||
1304 | fep->old_link = fep->link; | ||
1305 | |||
1306 | if (fep->link) { | ||
1307 | duplex = 0; | ||
1308 | if (fep->phy_status | ||
1309 | & (PHY_STAT_100FDX | PHY_STAT_10FDX)) | ||
1310 | duplex = 1; | ||
1311 | fec_restart(dev, duplex); | ||
1312 | } | ||
1313 | else | ||
1314 | fec_stop(dev); | ||
1315 | |||
1316 | #if 0 | ||
1317 | enable_irq(fep->mii_irq); | ||
1318 | #endif | ||
1319 | |||
1320 | } | ||
1321 | |||
1322 | static void mii_queue_relink(uint mii_reg, struct net_device *dev) | ||
1323 | { | ||
1324 | struct fec_enet_private *fep = dev->priv; | ||
1325 | |||
1326 | fep->phy_task.routine = (void *)mii_relink; | ||
1327 | fep->phy_task.data = dev; | ||
1328 | schedule_task(&fep->phy_task); | ||
1329 | } | ||
1330 | |||
1331 | static void mii_queue_config(uint mii_reg, struct net_device *dev) | ||
1332 | { | ||
1333 | struct fec_enet_private *fep = dev->priv; | ||
1334 | |||
1335 | fep->phy_task.routine = (void *)mii_display_config; | ||
1336 | fep->phy_task.data = dev; | ||
1337 | schedule_task(&fep->phy_task); | ||
1338 | } | ||
1339 | |||
1340 | |||
1341 | |||
1342 | phy_cmd_t phy_cmd_relink[] = { { mk_mii_read(MII_REG_CR), mii_queue_relink }, | ||
1343 | { mk_mii_end, } }; | ||
1344 | phy_cmd_t phy_cmd_config[] = { { mk_mii_read(MII_REG_CR), mii_queue_config }, | ||
1345 | { mk_mii_end, } }; | ||
1346 | |||
1347 | |||
1348 | |||
1349 | /* Read remainder of PHY ID. | ||
1350 | */ | ||
1351 | static void | ||
1352 | mii_discover_phy3(uint mii_reg, struct net_device *dev) | ||
1353 | { | ||
1354 | struct fec_enet_private *fep; | ||
1355 | int i; | ||
1356 | |||
1357 | fep = dev->priv; | ||
1358 | fep->phy_id |= (mii_reg & 0xffff); | ||
1359 | |||
1360 | for(i = 0; phy_info[i]; i++) | ||
1361 | if(phy_info[i]->id == (fep->phy_id >> 4)) | ||
1362 | break; | ||
1363 | |||
1364 | if(!phy_info[i]) | ||
1365 | panic("%s: PHY id 0x%08x is not supported!\n", | ||
1366 | dev->name, fep->phy_id); | ||
1367 | |||
1368 | fep->phy = phy_info[i]; | ||
1369 | fep->phy_id_done = 1; | ||
1370 | |||
1371 | printk("%s: Phy @ 0x%x, type %s (0x%08x)\n", | ||
1372 | dev->name, fep->phy_addr, fep->phy->name, fep->phy_id); | ||
1373 | } | ||
1374 | |||
1375 | /* Scan all of the MII PHY addresses looking for someone to respond | ||
1376 | * with a valid ID. This usually happens quickly. | ||
1377 | */ | ||
1378 | static void | ||
1379 | mii_discover_phy(uint mii_reg, struct net_device *dev) | ||
1380 | { | ||
1381 | struct fec_enet_private *fep; | ||
1382 | uint phytype; | ||
1383 | |||
1384 | fep = dev->priv; | ||
1385 | |||
1386 | if ((phytype = (mii_reg & 0xffff)) != 0xffff) { | ||
1387 | |||
1388 | /* Got first part of ID, now get remainder. | ||
1389 | */ | ||
1390 | fep->phy_id = phytype << 16; | ||
1391 | mii_queue(dev, mk_mii_read(MII_REG_PHYIR2), mii_discover_phy3); | ||
1392 | } else { | ||
1393 | fep->phy_addr++; | ||
1394 | if (fep->phy_addr < 32) { | ||
1395 | mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), | ||
1396 | mii_discover_phy); | ||
1397 | } else { | ||
1398 | printk("fec: No PHY device found.\n"); | ||
1399 | } | ||
1400 | } | ||
1401 | } | ||
1402 | #endif /* CONFIG_USE_MDIO */ | ||
1403 | |||
1404 | /* This interrupt occurs when the PHY detects a link change. | ||
1405 | */ | ||
1406 | static void | ||
1407 | #ifdef CONFIG_RPXCLASSIC | ||
1408 | mii_link_interrupt(void *dev_id) | ||
1409 | #else | ||
1410 | mii_link_interrupt(int irq, void * dev_id, struct pt_regs * regs) | ||
1411 | #endif | ||
1412 | { | ||
1413 | #ifdef CONFIG_USE_MDIO | ||
1414 | struct net_device *dev = dev_id; | ||
1415 | struct fec_enet_private *fep = dev->priv; | ||
1416 | volatile immap_t *immap = (immap_t *)IMAP_ADDR; | ||
1417 | volatile fec_t *fecp = &(immap->im_cpm.cp_fec); | ||
1418 | unsigned int ecntrl = fecp->fec_ecntrl; | ||
1419 | |||
1420 | /* We need the FEC enabled to access the MII | ||
1421 | */ | ||
1422 | if ((ecntrl & FEC_ECNTRL_ETHER_EN) == 0) { | ||
1423 | fecp->fec_ecntrl |= FEC_ECNTRL_ETHER_EN; | ||
1424 | } | ||
1425 | #endif /* CONFIG_USE_MDIO */ | ||
1426 | |||
1427 | #if 0 | ||
1428 | disable_irq(fep->mii_irq); /* disable now, enable later */ | ||
1429 | #endif | ||
1430 | |||
1431 | |||
1432 | #ifdef CONFIG_USE_MDIO | ||
1433 | mii_do_cmd(dev, fep->phy->ack_int); | ||
1434 | mii_do_cmd(dev, phy_cmd_relink); /* restart and display status */ | ||
1435 | |||
1436 | if ((ecntrl & FEC_ECNTRL_ETHER_EN) == 0) { | ||
1437 | fecp->fec_ecntrl = ecntrl; /* restore old settings */ | ||
1438 | } | ||
1439 | #else | ||
1440 | printk("%s[%d] %s: unexpected Link interrupt\n", __FILE__,__LINE__,__FUNCTION__); | ||
1441 | #endif /* CONFIG_USE_MDIO */ | ||
1442 | |||
1443 | } | ||
1444 | |||
1445 | static int | ||
1446 | fec_enet_open(struct net_device *dev) | ||
1447 | { | ||
1448 | struct fec_enet_private *fep = dev->priv; | ||
1449 | |||
1450 | /* I should reset the ring buffers here, but I don't yet know | ||
1451 | * a simple way to do that. | ||
1452 | */ | ||
1453 | |||
1454 | #ifdef CONFIG_USE_MDIO | ||
1455 | fep->sequence_done = 0; | ||
1456 | fep->link = 0; | ||
1457 | |||
1458 | if (fep->phy) { | ||
1459 | mii_do_cmd(dev, fep->phy->ack_int); | ||
1460 | mii_do_cmd(dev, fep->phy->config); | ||
1461 | mii_do_cmd(dev, phy_cmd_config); /* display configuration */ | ||
1462 | while(!fep->sequence_done) | ||
1463 | schedule(); | ||
1464 | |||
1465 | mii_do_cmd(dev, fep->phy->startup); | ||
1466 | netif_start_queue(dev); | ||
1467 | return 0; /* Success */ | ||
1468 | } | ||
1469 | return -ENODEV; /* No PHY we understand */ | ||
1470 | #else | ||
1471 | fep->link = 1; | ||
1472 | netif_start_queue(dev); | ||
1473 | return 0; /* Success */ | ||
1474 | #endif /* CONFIG_USE_MDIO */ | ||
1475 | |||
1476 | } | ||
1477 | |||
1478 | static int | ||
1479 | fec_enet_close(struct net_device *dev) | ||
1480 | { | ||
1481 | /* Don't know what to do yet. | ||
1482 | */ | ||
1483 | netif_stop_queue(dev); | ||
1484 | fec_stop(dev); | ||
1485 | |||
1486 | return 0; | ||
1487 | } | ||
1488 | |||
1489 | static struct net_device_stats *fec_enet_get_stats(struct net_device *dev) | ||
1490 | { | ||
1491 | struct fec_enet_private *fep = (struct fec_enet_private *)dev->priv; | ||
1492 | |||
1493 | return &fep->stats; | ||
1494 | } | ||
1495 | |||
1496 | /* Set or clear the multicast filter for this adaptor. | ||
1497 | * Skeleton taken from sunlance driver. | ||
1498 | * The CPM Ethernet implementation allows Multicast as well as individual | ||
1499 | * MAC address filtering. Some of the drivers check to make sure it is | ||
1500 | * a group multicast address, and discard those that are not. I guess I | ||
1501 | * will do the same for now, but just remove the test if you want | ||
1502 | * individual filtering as well (do the upper net layers want or support | ||
1503 | * this kind of feature?). | ||
1504 | */ | ||
1505 | |||
1506 | static void set_multicast_list(struct net_device *dev) | ||
1507 | { | ||
1508 | struct fec_enet_private *fep; | ||
1509 | volatile fec_t *ep; | ||
1510 | |||
1511 | fep = (struct fec_enet_private *)dev->priv; | ||
1512 | ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec); | ||
1513 | |||
1514 | if (dev->flags&IFF_PROMISC) { | ||
1515 | |||
1516 | /* Log any net taps. */ | ||
1517 | printk("%s: Promiscuous mode enabled.\n", dev->name); | ||
1518 | ep->fec_r_cntrl |= FEC_RCNTRL_PROM; | ||
1519 | } else { | ||
1520 | |||
1521 | ep->fec_r_cntrl &= ~FEC_RCNTRL_PROM; | ||
1522 | |||
1523 | if (dev->flags & IFF_ALLMULTI) { | ||
1524 | /* Catch all multicast addresses, so set the | ||
1525 | * filter to all 1's. | ||
1526 | */ | ||
1527 | ep->fec_hash_table_high = 0xffffffff; | ||
1528 | ep->fec_hash_table_low = 0xffffffff; | ||
1529 | } | ||
1530 | #if 0 | ||
1531 | else { | ||
1532 | /* Clear filter and add the addresses in the list. | ||
1533 | */ | ||
1534 | ep->sen_gaddr1 = 0; | ||
1535 | ep->sen_gaddr2 = 0; | ||
1536 | ep->sen_gaddr3 = 0; | ||
1537 | ep->sen_gaddr4 = 0; | ||
1538 | |||
1539 | dmi = dev->mc_list; | ||
1540 | |||
1541 | for (i=0; i<dev->mc_count; i++) { | ||
1542 | |||
1543 | /* Only support group multicast for now. | ||
1544 | */ | ||
1545 | if (!(dmi->dmi_addr[0] & 1)) | ||
1546 | continue; | ||
1547 | |||
1548 | /* The address in dmi_addr is LSB first, | ||
1549 | * and taddr is MSB first. We have to | ||
1550 | * copy bytes MSB first from dmi_addr. | ||
1551 | */ | ||
1552 | mcptr = (u_char *)dmi->dmi_addr + 5; | ||
1553 | tdptr = (u_char *)&ep->sen_taddrh; | ||
1554 | for (j=0; j<6; j++) | ||
1555 | *tdptr++ = *mcptr--; | ||
1556 | |||
1557 | /* Ask CPM to run CRC and set bit in | ||
1558 | * filter mask. | ||
1559 | */ | ||
1560 | cpmp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_SET_GADDR) | CPM_CR_FLG; | ||
1561 | /* this delay is necessary here -- Cort */ | ||
1562 | udelay(10); | ||
1563 | while (cpmp->cp_cpcr & CPM_CR_FLG); | ||
1564 | } | ||
1565 | } | ||
1566 | #endif | ||
1567 | } | ||
1568 | } | ||
1569 | |||
1570 | /* Initialize the FEC Ethernet on 860T. | ||
1571 | */ | ||
1572 | static int __init fec_enet_init(void) | ||
1573 | { | ||
1574 | struct net_device *dev; | ||
1575 | struct fec_enet_private *fep; | ||
1576 | int i, j, k, err; | ||
1577 | unsigned char *eap, *iap, *ba; | ||
1578 | unsigned long mem_addr; | ||
1579 | volatile cbd_t *bdp; | ||
1580 | cbd_t *cbd_base; | ||
1581 | volatile immap_t *immap; | ||
1582 | volatile fec_t *fecp; | ||
1583 | bd_t *bd; | ||
1584 | #ifdef CONFIG_SCC_ENET | ||
1585 | unsigned char tmpaddr[6]; | ||
1586 | #endif | ||
1587 | |||
1588 | immap = (immap_t *)IMAP_ADDR; /* pointer to internal registers */ | ||
1589 | |||
1590 | bd = (bd_t *)__res; | ||
1591 | |||
1592 | dev = alloc_etherdev(sizeof(*fep)); | ||
1593 | if (!dev) | ||
1594 | return -ENOMEM; | ||
1595 | |||
1596 | fep = dev->priv; | ||
1597 | |||
1598 | fecp = &(immap->im_cpm.cp_fec); | ||
1599 | |||
1600 | /* Whack a reset. We should wait for this. | ||
1601 | */ | ||
1602 | fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET; | ||
1603 | for (i = 0; | ||
1604 | (fecp->fec_ecntrl & FEC_ECNTRL_RESET) && (i < FEC_RESET_DELAY); | ||
1605 | ++i) { | ||
1606 | udelay(1); | ||
1607 | } | ||
1608 | if (i == FEC_RESET_DELAY) { | ||
1609 | printk ("FEC Reset timeout!\n"); | ||
1610 | } | ||
1611 | |||
1612 | /* Set the Ethernet address. If using multiple Enets on the 8xx, | ||
1613 | * this needs some work to get unique addresses. | ||
1614 | */ | ||
1615 | eap = (unsigned char *)my_enet_addr; | ||
1616 | iap = bd->bi_enetaddr; | ||
1617 | |||
1618 | #ifdef CONFIG_SCC_ENET | ||
1619 | /* | ||
1620 | * If a board has Ethernet configured both on a SCC and the | ||
1621 | * FEC, it needs (at least) 2 MAC addresses (we know that Sun | ||
1622 | * disagrees, but anyway). For the FEC port, we create | ||
1623 | * another address by setting one of the address bits above | ||
1624 | * something that would have (up to now) been allocated. | ||
1625 | */ | ||
1626 | for (i=0; i<6; i++) | ||
1627 | tmpaddr[i] = *iap++; | ||
1628 | tmpaddr[3] |= 0x80; | ||
1629 | iap = tmpaddr; | ||
1630 | #endif | ||
1631 | |||
1632 | for (i=0; i<6; i++) { | ||
1633 | dev->dev_addr[i] = *eap++ = *iap++; | ||
1634 | } | ||
1635 | |||
1636 | /* Allocate memory for buffer descriptors. | ||
1637 | */ | ||
1638 | if (((RX_RING_SIZE + TX_RING_SIZE) * sizeof(cbd_t)) > PAGE_SIZE) { | ||
1639 | printk("FEC init error. Need more space.\n"); | ||
1640 | printk("FEC initialization failed.\n"); | ||
1641 | return 1; | ||
1642 | } | ||
1643 | cbd_base = (cbd_t *)consistent_alloc(GFP_KERNEL, PAGE_SIZE, &mem_addr); | ||
1644 | |||
1645 | /* Set receive and transmit descriptor base. | ||
1646 | */ | ||
1647 | fep->rx_bd_base = cbd_base; | ||
1648 | fep->tx_bd_base = cbd_base + RX_RING_SIZE; | ||
1649 | |||
1650 | fep->skb_cur = fep->skb_dirty = 0; | ||
1651 | |||
1652 | /* Initialize the receive buffer descriptors. | ||
1653 | */ | ||
1654 | bdp = fep->rx_bd_base; | ||
1655 | k = 0; | ||
1656 | for (i=0; i<FEC_ENET_RX_PAGES; i++) { | ||
1657 | |||
1658 | /* Allocate a page. | ||
1659 | */ | ||
1660 | ba = (unsigned char *)consistent_alloc(GFP_KERNEL, PAGE_SIZE, &mem_addr); | ||
1661 | /* BUG: no check for failure */ | ||
1662 | |||
1663 | /* Initialize the BD for every fragment in the page. | ||
1664 | */ | ||
1665 | for (j=0; j<FEC_ENET_RX_FRPPG; j++) { | ||
1666 | bdp->cbd_sc = BD_ENET_RX_EMPTY; | ||
1667 | bdp->cbd_bufaddr = mem_addr; | ||
1668 | fep->rx_vaddr[k++] = ba; | ||
1669 | mem_addr += FEC_ENET_RX_FRSIZE; | ||
1670 | ba += FEC_ENET_RX_FRSIZE; | ||
1671 | bdp++; | ||
1672 | } | ||
1673 | } | ||
1674 | |||
1675 | /* Set the last buffer to wrap. | ||
1676 | */ | ||
1677 | bdp--; | ||
1678 | bdp->cbd_sc |= BD_SC_WRAP; | ||
1679 | |||
1680 | #ifdef CONFIG_FEC_PACKETHOOK | ||
1681 | fep->ph_lock = 0; | ||
1682 | fep->ph_rxhandler = fep->ph_txhandler = NULL; | ||
1683 | fep->ph_proto = 0; | ||
1684 | fep->ph_regaddr = NULL; | ||
1685 | fep->ph_priv = NULL; | ||
1686 | #endif | ||
1687 | |||
1688 | /* Install our interrupt handler. | ||
1689 | */ | ||
1690 | if (request_irq(FEC_INTERRUPT, fec_enet_interrupt, 0, "fec", dev) != 0) | ||
1691 | panic("Could not allocate FEC IRQ!"); | ||
1692 | |||
1693 | #ifdef CONFIG_RPXCLASSIC | ||
1694 | /* Make Port C, bit 15 an input that causes interrupts. | ||
1695 | */ | ||
1696 | immap->im_ioport.iop_pcpar &= ~0x0001; | ||
1697 | immap->im_ioport.iop_pcdir &= ~0x0001; | ||
1698 | immap->im_ioport.iop_pcso &= ~0x0001; | ||
1699 | immap->im_ioport.iop_pcint |= 0x0001; | ||
1700 | cpm_install_handler(CPMVEC_PIO_PC15, mii_link_interrupt, dev); | ||
1701 | |||
1702 | /* Make LEDS reflect Link status. | ||
1703 | */ | ||
1704 | *((uint *) RPX_CSR_ADDR) &= ~BCSR2_FETHLEDMODE; | ||
1705 | #endif | ||
1706 | |||
1707 | #ifdef PHY_INTERRUPT | ||
1708 | ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_siel |= | ||
1709 | (0x80000000 >> PHY_INTERRUPT); | ||
1710 | |||
1711 | if (request_irq(PHY_INTERRUPT, mii_link_interrupt, 0, "mii", dev) != 0) | ||
1712 | panic("Could not allocate MII IRQ!"); | ||
1713 | #endif | ||
1714 | |||
1715 | dev->base_addr = (unsigned long)fecp; | ||
1716 | |||
1717 | /* The FEC Ethernet specific entries in the device structure. */ | ||
1718 | dev->open = fec_enet_open; | ||
1719 | dev->hard_start_xmit = fec_enet_start_xmit; | ||
1720 | dev->tx_timeout = fec_timeout; | ||
1721 | dev->watchdog_timeo = TX_TIMEOUT; | ||
1722 | dev->stop = fec_enet_close; | ||
1723 | dev->get_stats = fec_enet_get_stats; | ||
1724 | dev->set_multicast_list = set_multicast_list; | ||
1725 | |||
1726 | #ifdef CONFIG_USE_MDIO | ||
1727 | for (i=0; i<NMII-1; i++) | ||
1728 | mii_cmds[i].mii_next = &mii_cmds[i+1]; | ||
1729 | mii_free = mii_cmds; | ||
1730 | #endif /* CONFIG_USE_MDIO */ | ||
1731 | |||
1732 | /* Configure all of port D for MII. | ||
1733 | */ | ||
1734 | immap->im_ioport.iop_pdpar = 0x1fff; | ||
1735 | |||
1736 | /* Bits moved from Rev. D onward. | ||
1737 | */ | ||
1738 | if ((mfspr(SPRN_IMMR) & 0xffff) < 0x0501) | ||
1739 | immap->im_ioport.iop_pddir = 0x1c58; /* Pre rev. D */ | ||
1740 | else | ||
1741 | immap->im_ioport.iop_pddir = 0x1fff; /* Rev. D and later */ | ||
1742 | |||
1743 | #ifdef CONFIG_USE_MDIO | ||
1744 | /* Set MII speed to 2.5 MHz | ||
1745 | */ | ||
1746 | fecp->fec_mii_speed = fep->phy_speed = | ||
1747 | (( (bd->bi_intfreq + 500000) / 2500000 / 2 ) & 0x3F ) << 1; | ||
1748 | #else | ||
1749 | fecp->fec_mii_speed = 0; /* turn off MDIO */ | ||
1750 | #endif /* CONFIG_USE_MDIO */ | ||
1751 | |||
1752 | err = register_netdev(dev); | ||
1753 | if (err) { | ||
1754 | free_netdev(dev); | ||
1755 | return err; | ||
1756 | } | ||
1757 | |||
1758 | printk ("%s: FEC ENET Version 0.2, FEC irq %d" | ||
1759 | #ifdef PHY_INTERRUPT | ||
1760 | ", MII irq %d" | ||
1761 | #endif | ||
1762 | ", addr ", | ||
1763 | dev->name, FEC_INTERRUPT | ||
1764 | #ifdef PHY_INTERRUPT | ||
1765 | , PHY_INTERRUPT | ||
1766 | #endif | ||
1767 | ); | ||
1768 | for (i=0; i<6; i++) | ||
1769 | printk("%02x%c", dev->dev_addr[i], (i==5) ? '\n' : ':'); | ||
1770 | |||
1771 | #ifdef CONFIG_USE_MDIO /* start in full duplex mode, and negotiate speed */ | ||
1772 | fec_restart (dev, 1); | ||
1773 | #else /* always use half duplex mode only */ | ||
1774 | fec_restart (dev, 0); | ||
1775 | #endif | ||
1776 | |||
1777 | #ifdef CONFIG_USE_MDIO | ||
1778 | /* Queue up command to detect the PHY and initialize the | ||
1779 | * remainder of the interface. | ||
1780 | */ | ||
1781 | fep->phy_id_done = 0; | ||
1782 | fep->phy_addr = 0; | ||
1783 | mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy); | ||
1784 | #endif /* CONFIG_USE_MDIO */ | ||
1785 | |||
1786 | return 0; | ||
1787 | } | ||
1788 | module_init(fec_enet_init); | ||
1789 | |||
1790 | /* This function is called to start or restart the FEC during a link | ||
1791 | * change. This only happens when switching between half and full | ||
1792 | * duplex. | ||
1793 | */ | ||
1794 | static void | ||
1795 | fec_restart(struct net_device *dev, int duplex) | ||
1796 | { | ||
1797 | struct fec_enet_private *fep; | ||
1798 | int i; | ||
1799 | volatile cbd_t *bdp; | ||
1800 | volatile immap_t *immap; | ||
1801 | volatile fec_t *fecp; | ||
1802 | |||
1803 | immap = (immap_t *)IMAP_ADDR; /* pointer to internal registers */ | ||
1804 | |||
1805 | fecp = &(immap->im_cpm.cp_fec); | ||
1806 | |||
1807 | fep = dev->priv; | ||
1808 | |||
1809 | /* Whack a reset. We should wait for this. | ||
1810 | */ | ||
1811 | fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET; | ||
1812 | for (i = 0; | ||
1813 | (fecp->fec_ecntrl & FEC_ECNTRL_RESET) && (i < FEC_RESET_DELAY); | ||
1814 | ++i) { | ||
1815 | udelay(1); | ||
1816 | } | ||
1817 | if (i == FEC_RESET_DELAY) { | ||
1818 | printk ("FEC Reset timeout!\n"); | ||
1819 | } | ||
1820 | |||
1821 | /* Set station address. | ||
1822 | */ | ||
1823 | fecp->fec_addr_low = (my_enet_addr[0] << 16) | my_enet_addr[1]; | ||
1824 | fecp->fec_addr_high = my_enet_addr[2]; | ||
1825 | |||
1826 | /* Reset all multicast. | ||
1827 | */ | ||
1828 | fecp->fec_hash_table_high = 0; | ||
1829 | fecp->fec_hash_table_low = 0; | ||
1830 | |||
1831 | /* Set maximum receive buffer size. | ||
1832 | */ | ||
1833 | fecp->fec_r_buff_size = PKT_MAXBLR_SIZE; | ||
1834 | fecp->fec_r_hash = PKT_MAXBUF_SIZE; | ||
1835 | |||
1836 | /* Set receive and transmit descriptor base. | ||
1837 | */ | ||
1838 | fecp->fec_r_des_start = iopa((uint)(fep->rx_bd_base)); | ||
1839 | fecp->fec_x_des_start = iopa((uint)(fep->tx_bd_base)); | ||
1840 | |||
1841 | fep->dirty_tx = fep->cur_tx = fep->tx_bd_base; | ||
1842 | fep->cur_rx = fep->rx_bd_base; | ||
1843 | |||
1844 | /* Reset SKB transmit buffers. | ||
1845 | */ | ||
1846 | fep->skb_cur = fep->skb_dirty = 0; | ||
1847 | for (i=0; i<=TX_RING_MOD_MASK; i++) { | ||
1848 | if (fep->tx_skbuff[i] != NULL) { | ||
1849 | dev_kfree_skb(fep->tx_skbuff[i]); | ||
1850 | fep->tx_skbuff[i] = NULL; | ||
1851 | } | ||
1852 | } | ||
1853 | |||
1854 | /* Initialize the receive buffer descriptors. | ||
1855 | */ | ||
1856 | bdp = fep->rx_bd_base; | ||
1857 | for (i=0; i<RX_RING_SIZE; i++) { | ||
1858 | |||
1859 | /* Initialize the BD for every fragment in the page. | ||
1860 | */ | ||
1861 | bdp->cbd_sc = BD_ENET_RX_EMPTY; | ||
1862 | bdp++; | ||
1863 | } | ||
1864 | |||
1865 | /* Set the last buffer to wrap. | ||
1866 | */ | ||
1867 | bdp--; | ||
1868 | bdp->cbd_sc |= BD_SC_WRAP; | ||
1869 | |||
1870 | /* ...and the same for transmmit. | ||
1871 | */ | ||
1872 | bdp = fep->tx_bd_base; | ||
1873 | for (i=0; i<TX_RING_SIZE; i++) { | ||
1874 | |||
1875 | /* Initialize the BD for every fragment in the page. | ||
1876 | */ | ||
1877 | bdp->cbd_sc = 0; | ||
1878 | bdp->cbd_bufaddr = 0; | ||
1879 | bdp++; | ||
1880 | } | ||
1881 | |||
1882 | /* Set the last buffer to wrap. | ||
1883 | */ | ||
1884 | bdp--; | ||
1885 | bdp->cbd_sc |= BD_SC_WRAP; | ||
1886 | |||
1887 | /* Enable MII mode. | ||
1888 | */ | ||
1889 | if (duplex) { | ||
1890 | fecp->fec_r_cntrl = FEC_RCNTRL_MII_MODE; /* MII enable */ | ||
1891 | fecp->fec_x_cntrl = FEC_TCNTRL_FDEN; /* FD enable */ | ||
1892 | } | ||
1893 | else { | ||
1894 | fecp->fec_r_cntrl = FEC_RCNTRL_MII_MODE | FEC_RCNTRL_DRT; | ||
1895 | fecp->fec_x_cntrl = 0; | ||
1896 | } | ||
1897 | fep->full_duplex = duplex; | ||
1898 | |||
1899 | /* Enable big endian and don't care about SDMA FC. | ||
1900 | */ | ||
1901 | fecp->fec_fun_code = 0x78000000; | ||
1902 | |||
1903 | #ifdef CONFIG_USE_MDIO | ||
1904 | /* Set MII speed. | ||
1905 | */ | ||
1906 | fecp->fec_mii_speed = fep->phy_speed; | ||
1907 | #endif /* CONFIG_USE_MDIO */ | ||
1908 | |||
1909 | /* Clear any outstanding interrupt. | ||
1910 | */ | ||
1911 | fecp->fec_ievent = 0xffc0; | ||
1912 | |||
1913 | fecp->fec_ivec = (FEC_INTERRUPT/2) << 29; | ||
1914 | |||
1915 | /* Enable interrupts we wish to service. | ||
1916 | */ | ||
1917 | fecp->fec_imask = ( FEC_ENET_TXF | FEC_ENET_TXB | | ||
1918 | FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII ); | ||
1919 | |||
1920 | /* And last, enable the transmit and receive processing. | ||
1921 | */ | ||
1922 | fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN; | ||
1923 | fecp->fec_r_des_active = 0x01000000; | ||
1924 | } | ||
1925 | |||
1926 | static void | ||
1927 | fec_stop(struct net_device *dev) | ||
1928 | { | ||
1929 | volatile immap_t *immap; | ||
1930 | volatile fec_t *fecp; | ||
1931 | struct fec_enet_private *fep; | ||
1932 | int i; | ||
1933 | |||
1934 | immap = (immap_t *)IMAP_ADDR; /* pointer to internal registers */ | ||
1935 | |||
1936 | fecp = &(immap->im_cpm.cp_fec); | ||
1937 | |||
1938 | if ((fecp->fec_ecntrl & FEC_ECNTRL_ETHER_EN) == 0) | ||
1939 | return; /* already down */ | ||
1940 | |||
1941 | fep = dev->priv; | ||
1942 | |||
1943 | |||
1944 | fecp->fec_x_cntrl = 0x01; /* Graceful transmit stop */ | ||
1945 | |||
1946 | for (i = 0; | ||
1947 | ((fecp->fec_ievent & 0x10000000) == 0) && (i < FEC_RESET_DELAY); | ||
1948 | ++i) { | ||
1949 | udelay(1); | ||
1950 | } | ||
1951 | if (i == FEC_RESET_DELAY) { | ||
1952 | printk ("FEC timeout on graceful transmit stop\n"); | ||
1953 | } | ||
1954 | |||
1955 | /* Clear outstanding MII command interrupts. | ||
1956 | */ | ||
1957 | fecp->fec_ievent = FEC_ENET_MII; | ||
1958 | |||
1959 | /* Enable MII command finished interrupt | ||
1960 | */ | ||
1961 | fecp->fec_ivec = (FEC_INTERRUPT/2) << 29; | ||
1962 | fecp->fec_imask = FEC_ENET_MII; | ||
1963 | |||
1964 | #ifdef CONFIG_USE_MDIO | ||
1965 | /* Set MII speed. | ||
1966 | */ | ||
1967 | fecp->fec_mii_speed = fep->phy_speed; | ||
1968 | #endif /* CONFIG_USE_MDIO */ | ||
1969 | |||
1970 | /* Disable FEC | ||
1971 | */ | ||
1972 | fecp->fec_ecntrl &= ~(FEC_ECNTRL_ETHER_EN); | ||
1973 | } | ||
diff --git a/arch/ppc/8xx_io/micropatch.c b/arch/ppc/8xx_io/micropatch.c new file mode 100644 index 000000000000..312af0776c31 --- /dev/null +++ b/arch/ppc/8xx_io/micropatch.c | |||
@@ -0,0 +1,744 @@ | |||
1 | |||
2 | /* Microcode patches for the CPM as supplied by Motorola. | ||
3 | * This is the one for IIC/SPI. There is a newer one that | ||
4 | * also relocates SMC2, but this would require additional changes | ||
5 | * to uart.c, so I am holding off on that for a moment. | ||
6 | */ | ||
7 | #include <linux/config.h> | ||
8 | #include <linux/errno.h> | ||
9 | #include <linux/sched.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/param.h> | ||
12 | #include <linux/string.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <asm/irq.h> | ||
16 | #include <asm/mpc8xx.h> | ||
17 | #include <asm/page.h> | ||
18 | #include <asm/pgtable.h> | ||
19 | #include <asm/8xx_immap.h> | ||
20 | #include <asm/commproc.h> | ||
21 | |||
22 | /* | ||
23 | * I2C/SPI relocation patch arrays. | ||
24 | */ | ||
25 | |||
26 | #ifdef CONFIG_I2C_SPI_UCODE_PATCH | ||
27 | |||
28 | uint patch_2000[] = { | ||
29 | 0x7FFFEFD9, | ||
30 | 0x3FFD0000, | ||
31 | 0x7FFB49F7, | ||
32 | 0x7FF90000, | ||
33 | 0x5FEFADF7, | ||
34 | 0x5F89ADF7, | ||
35 | 0x5FEFAFF7, | ||
36 | 0x5F89AFF7, | ||
37 | 0x3A9CFBC8, | ||
38 | 0xE7C0EDF0, | ||
39 | 0x77C1E1BB, | ||
40 | 0xF4DC7F1D, | ||
41 | 0xABAD932F, | ||
42 | 0x4E08FDCF, | ||
43 | 0x6E0FAFF8, | ||
44 | 0x7CCF76CF, | ||
45 | 0xFD1FF9CF, | ||
46 | 0xABF88DC6, | ||
47 | 0xAB5679F7, | ||
48 | 0xB0937383, | ||
49 | 0xDFCE79F7, | ||
50 | 0xB091E6BB, | ||
51 | 0xE5BBE74F, | ||
52 | 0xB3FA6F0F, | ||
53 | 0x6FFB76CE, | ||
54 | 0xEE0DF9CF, | ||
55 | 0x2BFBEFEF, | ||
56 | 0xCFEEF9CF, | ||
57 | 0x76CEAD24, | ||
58 | 0x90B2DF9A, | ||
59 | 0x7FDDD0BF, | ||
60 | 0x4BF847FD, | ||
61 | 0x7CCF76CE, | ||
62 | 0xCFEF7E1F, | ||
63 | 0x7F1D7DFD, | ||
64 | 0xF0B6EF71, | ||
65 | 0x7FC177C1, | ||
66 | 0xFBC86079, | ||
67 | 0xE722FBC8, | ||
68 | 0x5FFFDFFF, | ||
69 | 0x5FB2FFFB, | ||
70 | 0xFBC8F3C8, | ||
71 | 0x94A67F01, | ||
72 | 0x7F1D5F39, | ||
73 | 0xAFE85F5E, | ||
74 | 0xFFDFDF96, | ||
75 | 0xCB9FAF7D, | ||
76 | 0x5FC1AFED, | ||
77 | 0x8C1C5FC1, | ||
78 | 0xAFDD5FC3, | ||
79 | 0xDF9A7EFD, | ||
80 | 0xB0B25FB2, | ||
81 | 0xFFFEABAD, | ||
82 | 0x5FB2FFFE, | ||
83 | 0x5FCE600B, | ||
84 | 0xE6BB600B, | ||
85 | 0x5FCEDFC6, | ||
86 | 0x27FBEFDF, | ||
87 | 0x5FC8CFDE, | ||
88 | 0x3A9CE7C0, | ||
89 | 0xEDF0F3C8, | ||
90 | 0x7F0154CD, | ||
91 | 0x7F1D2D3D, | ||
92 | 0x363A7570, | ||
93 | 0x7E0AF1CE, | ||
94 | 0x37EF2E68, | ||
95 | 0x7FEE10EC, | ||
96 | 0xADF8EFDE, | ||
97 | 0xCFEAE52F, | ||
98 | 0x7D0FE12B, | ||
99 | 0xF1CE5F65, | ||
100 | 0x7E0A4DF8, | ||
101 | 0xCFEA5F72, | ||
102 | 0x7D0BEFEE, | ||
103 | 0xCFEA5F74, | ||
104 | 0xE522EFDE, | ||
105 | 0x5F74CFDA, | ||
106 | 0x0B627385, | ||
107 | 0xDF627E0A, | ||
108 | 0x30D8145B, | ||
109 | 0xBFFFF3C8, | ||
110 | 0x5FFFDFFF, | ||
111 | 0xA7F85F5E, | ||
112 | 0xBFFE7F7D, | ||
113 | 0x10D31450, | ||
114 | 0x5F36BFFF, | ||
115 | 0xAF785F5E, | ||
116 | 0xBFFDA7F8, | ||
117 | 0x5F36BFFE, | ||
118 | 0x77FD30C0, | ||
119 | 0x4E08FDCF, | ||
120 | 0xE5FF6E0F, | ||
121 | 0xAFF87E1F, | ||
122 | 0x7E0FFD1F, | ||
123 | 0xF1CF5F1B, | ||
124 | 0xABF80D5E, | ||
125 | 0x5F5EFFEF, | ||
126 | 0x79F730A2, | ||
127 | 0xAFDD5F34, | ||
128 | 0x47F85F34, | ||
129 | 0xAFED7FDD, | ||
130 | 0x50B24978, | ||
131 | 0x47FD7F1D, | ||
132 | 0x7DFD70AD, | ||
133 | 0xEF717EC1, | ||
134 | 0x6BA47F01, | ||
135 | 0x2D267EFD, | ||
136 | 0x30DE5F5E, | ||
137 | 0xFFFD5F5E, | ||
138 | 0xFFEF5F5E, | ||
139 | 0xFFDF0CA0, | ||
140 | 0xAFED0A9E, | ||
141 | 0xAFDD0C3A, | ||
142 | 0x5F3AAFBD, | ||
143 | 0x7FBDB082, | ||
144 | 0x5F8247F8 | ||
145 | }; | ||
146 | |||
147 | uint patch_2f00[] = { | ||
148 | 0x3E303430, | ||
149 | 0x34343737, | ||
150 | 0xABF7BF9B, | ||
151 | 0x994B4FBD, | ||
152 | 0xBD599493, | ||
153 | 0x349FFF37, | ||
154 | 0xFB9B177D, | ||
155 | 0xD9936956, | ||
156 | 0xBBFDD697, | ||
157 | 0xBDD2FD11, | ||
158 | 0x31DB9BB3, | ||
159 | 0x63139637, | ||
160 | 0x93733693, | ||
161 | 0x193137F7, | ||
162 | 0x331737AF, | ||
163 | 0x7BB9B999, | ||
164 | 0xBB197957, | ||
165 | 0x7FDFD3D5, | ||
166 | 0x73B773F7, | ||
167 | 0x37933B99, | ||
168 | 0x1D115316, | ||
169 | 0x99315315, | ||
170 | 0x31694BF4, | ||
171 | 0xFBDBD359, | ||
172 | 0x31497353, | ||
173 | 0x76956D69, | ||
174 | 0x7B9D9693, | ||
175 | 0x13131979, | ||
176 | 0x79376935 | ||
177 | }; | ||
178 | #endif | ||
179 | |||
180 | /* | ||
181 | * I2C/SPI/SMC1 relocation patch arrays. | ||
182 | */ | ||
183 | |||
184 | #ifdef CONFIG_I2C_SPI_SMC1_UCODE_PATCH | ||
185 | |||
186 | uint patch_2000[] = { | ||
187 | 0x3fff0000, | ||
188 | 0x3ffd0000, | ||
189 | 0x3ffb0000, | ||
190 | 0x3ff90000, | ||
191 | 0x5f13eff8, | ||
192 | 0x5eb5eff8, | ||
193 | 0x5f88adf7, | ||
194 | 0x5fefadf7, | ||
195 | 0x3a9cfbc8, | ||
196 | 0x77cae1bb, | ||
197 | 0xf4de7fad, | ||
198 | 0xabae9330, | ||
199 | 0x4e08fdcf, | ||
200 | 0x6e0faff8, | ||
201 | 0x7ccf76cf, | ||
202 | 0xfdaff9cf, | ||
203 | 0xabf88dc8, | ||
204 | 0xab5879f7, | ||
205 | 0xb0925d8d, | ||
206 | 0xdfd079f7, | ||
207 | 0xb090e6bb, | ||
208 | 0xe5bbe74f, | ||
209 | 0x9e046f0f, | ||
210 | 0x6ffb76ce, | ||
211 | 0xee0cf9cf, | ||
212 | 0x2bfbefef, | ||
213 | 0xcfeef9cf, | ||
214 | 0x76cead23, | ||
215 | 0x90b3df99, | ||
216 | 0x7fddd0c1, | ||
217 | 0x4bf847fd, | ||
218 | 0x7ccf76ce, | ||
219 | 0xcfef77ca, | ||
220 | 0x7eaf7fad, | ||
221 | 0x7dfdf0b7, | ||
222 | 0xef7a7fca, | ||
223 | 0x77cafbc8, | ||
224 | 0x6079e722, | ||
225 | 0xfbc85fff, | ||
226 | 0xdfff5fb3, | ||
227 | 0xfffbfbc8, | ||
228 | 0xf3c894a5, | ||
229 | 0xe7c9edf9, | ||
230 | 0x7f9a7fad, | ||
231 | 0x5f36afe8, | ||
232 | 0x5f5bffdf, | ||
233 | 0xdf95cb9e, | ||
234 | 0xaf7d5fc3, | ||
235 | 0xafed8c1b, | ||
236 | 0x5fc3afdd, | ||
237 | 0x5fc5df99, | ||
238 | 0x7efdb0b3, | ||
239 | 0x5fb3fffe, | ||
240 | 0xabae5fb3, | ||
241 | 0xfffe5fd0, | ||
242 | 0x600be6bb, | ||
243 | 0x600b5fd0, | ||
244 | 0xdfc827fb, | ||
245 | 0xefdf5fca, | ||
246 | 0xcfde3a9c, | ||
247 | 0xe7c9edf9, | ||
248 | 0xf3c87f9e, | ||
249 | 0x54ca7fed, | ||
250 | 0x2d3a3637, | ||
251 | 0x756f7e9a, | ||
252 | 0xf1ce37ef, | ||
253 | 0x2e677fee, | ||
254 | 0x10ebadf8, | ||
255 | 0xefdecfea, | ||
256 | 0xe52f7d9f, | ||
257 | 0xe12bf1ce, | ||
258 | 0x5f647e9a, | ||
259 | 0x4df8cfea, | ||
260 | 0x5f717d9b, | ||
261 | 0xefeecfea, | ||
262 | 0x5f73e522, | ||
263 | 0xefde5f73, | ||
264 | 0xcfda0b61, | ||
265 | 0x5d8fdf61, | ||
266 | 0xe7c9edf9, | ||
267 | 0x7e9a30d5, | ||
268 | 0x1458bfff, | ||
269 | 0xf3c85fff, | ||
270 | 0xdfffa7f8, | ||
271 | 0x5f5bbffe, | ||
272 | 0x7f7d10d0, | ||
273 | 0x144d5f33, | ||
274 | 0xbfffaf78, | ||
275 | 0x5f5bbffd, | ||
276 | 0xa7f85f33, | ||
277 | 0xbffe77fd, | ||
278 | 0x30bd4e08, | ||
279 | 0xfdcfe5ff, | ||
280 | 0x6e0faff8, | ||
281 | 0x7eef7e9f, | ||
282 | 0xfdeff1cf, | ||
283 | 0x5f17abf8, | ||
284 | 0x0d5b5f5b, | ||
285 | 0xffef79f7, | ||
286 | 0x309eafdd, | ||
287 | 0x5f3147f8, | ||
288 | 0x5f31afed, | ||
289 | 0x7fdd50af, | ||
290 | 0x497847fd, | ||
291 | 0x7f9e7fed, | ||
292 | 0x7dfd70a9, | ||
293 | 0xef7e7ece, | ||
294 | 0x6ba07f9e, | ||
295 | 0x2d227efd, | ||
296 | 0x30db5f5b, | ||
297 | 0xfffd5f5b, | ||
298 | 0xffef5f5b, | ||
299 | 0xffdf0c9c, | ||
300 | 0xafed0a9a, | ||
301 | 0xafdd0c37, | ||
302 | 0x5f37afbd, | ||
303 | 0x7fbdb081, | ||
304 | 0x5f8147f8, | ||
305 | 0x3a11e710, | ||
306 | 0xedf0ccdd, | ||
307 | 0xf3186d0a, | ||
308 | 0x7f0e5f06, | ||
309 | 0x7fedbb38, | ||
310 | 0x3afe7468, | ||
311 | 0x7fedf4fc, | ||
312 | 0x8ffbb951, | ||
313 | 0xb85f77fd, | ||
314 | 0xb0df5ddd, | ||
315 | 0xdefe7fed, | ||
316 | 0x90e1e74d, | ||
317 | 0x6f0dcbf7, | ||
318 | 0xe7decfed, | ||
319 | 0xcb74cfed, | ||
320 | 0xcfeddf6d, | ||
321 | 0x91714f74, | ||
322 | 0x5dd2deef, | ||
323 | 0x9e04e7df, | ||
324 | 0xefbb6ffb, | ||
325 | 0xe7ef7f0e, | ||
326 | 0x9e097fed, | ||
327 | 0xebdbeffa, | ||
328 | 0xeb54affb, | ||
329 | 0x7fea90d7, | ||
330 | 0x7e0cf0c3, | ||
331 | 0xbffff318, | ||
332 | 0x5fffdfff, | ||
333 | 0xac59efea, | ||
334 | 0x7fce1ee5, | ||
335 | 0xe2ff5ee1, | ||
336 | 0xaffbe2ff, | ||
337 | 0x5ee3affb, | ||
338 | 0xf9cc7d0f, | ||
339 | 0xaef8770f, | ||
340 | 0x7d0fb0c6, | ||
341 | 0xeffbbfff, | ||
342 | 0xcfef5ede, | ||
343 | 0x7d0fbfff, | ||
344 | 0x5ede4cf8, | ||
345 | 0x7fddd0bf, | ||
346 | 0x49f847fd, | ||
347 | 0x7efdf0bb, | ||
348 | 0x7fedfffd, | ||
349 | 0x7dfdf0b7, | ||
350 | 0xef7e7e1e, | ||
351 | 0x5ede7f0e, | ||
352 | 0x3a11e710, | ||
353 | 0xedf0ccab, | ||
354 | 0xfb18ad2e, | ||
355 | 0x1ea9bbb8, | ||
356 | 0x74283b7e, | ||
357 | 0x73c2e4bb, | ||
358 | 0x2ada4fb8, | ||
359 | 0xdc21e4bb, | ||
360 | 0xb2a1ffbf, | ||
361 | 0x5e2c43f8, | ||
362 | 0xfc87e1bb, | ||
363 | 0xe74ffd91, | ||
364 | 0x6f0f4fe8, | ||
365 | 0xc7ba32e2, | ||
366 | 0xf396efeb, | ||
367 | 0x600b4f78, | ||
368 | 0xe5bb760b, | ||
369 | 0x53acaef8, | ||
370 | 0x4ef88b0e, | ||
371 | 0xcfef9e09, | ||
372 | 0xabf8751f, | ||
373 | 0xefef5bac, | ||
374 | 0x741f4fe8, | ||
375 | 0x751e760d, | ||
376 | 0x7fdbf081, | ||
377 | 0x741cafce, | ||
378 | 0xefcc7fce, | ||
379 | 0x751e70ac, | ||
380 | 0x741ce7bb, | ||
381 | 0x3372cfed, | ||
382 | 0xafdbefeb, | ||
383 | 0xe5bb760b, | ||
384 | 0x53f2aef8, | ||
385 | 0xafe8e7eb, | ||
386 | 0x4bf8771e, | ||
387 | 0x7e247fed, | ||
388 | 0x4fcbe2cc, | ||
389 | 0x7fbc30a9, | ||
390 | 0x7b0f7a0f, | ||
391 | 0x34d577fd, | ||
392 | 0x308b5db7, | ||
393 | 0xde553e5f, | ||
394 | 0xaf78741f, | ||
395 | 0x741f30f0, | ||
396 | 0xcfef5e2c, | ||
397 | 0x741f3eac, | ||
398 | 0xafb8771e, | ||
399 | 0x5e677fed, | ||
400 | 0x0bd3e2cc, | ||
401 | 0x741ccfec, | ||
402 | 0xe5ca53cd, | ||
403 | 0x6fcb4f74, | ||
404 | 0x5dadde4b, | ||
405 | 0x2ab63d38, | ||
406 | 0x4bb3de30, | ||
407 | 0x751f741c, | ||
408 | 0x6c42effa, | ||
409 | 0xefea7fce, | ||
410 | 0x6ffc30be, | ||
411 | 0xefec3fca, | ||
412 | 0x30b3de2e, | ||
413 | 0xadf85d9e, | ||
414 | 0xaf7daefd, | ||
415 | 0x5d9ede2e, | ||
416 | 0x5d9eafdd, | ||
417 | 0x761f10ac, | ||
418 | 0x1da07efd, | ||
419 | 0x30adfffe, | ||
420 | 0x4908fb18, | ||
421 | 0x5fffdfff, | ||
422 | 0xafbb709b, | ||
423 | 0x4ef85e67, | ||
424 | 0xadf814ad, | ||
425 | 0x7a0f70ad, | ||
426 | 0xcfef50ad, | ||
427 | 0x7a0fde30, | ||
428 | 0x5da0afed, | ||
429 | 0x3c12780f, | ||
430 | 0xefef780f, | ||
431 | 0xefef790f, | ||
432 | 0xa7f85e0f, | ||
433 | 0xffef790f, | ||
434 | 0xefef790f, | ||
435 | 0x14adde2e, | ||
436 | 0x5d9eadfd, | ||
437 | 0x5e2dfffb, | ||
438 | 0xe79addfd, | ||
439 | 0xeff96079, | ||
440 | 0x607ae79a, | ||
441 | 0xddfceff9, | ||
442 | 0x60795dff, | ||
443 | 0x607acfef, | ||
444 | 0xefefefdf, | ||
445 | 0xefbfef7f, | ||
446 | 0xeeffedff, | ||
447 | 0xebffe7ff, | ||
448 | 0xafefafdf, | ||
449 | 0xafbfaf7f, | ||
450 | 0xaeffadff, | ||
451 | 0xabffa7ff, | ||
452 | 0x6fef6fdf, | ||
453 | 0x6fbf6f7f, | ||
454 | 0x6eff6dff, | ||
455 | 0x6bff67ff, | ||
456 | 0x2fef2fdf, | ||
457 | 0x2fbf2f7f, | ||
458 | 0x2eff2dff, | ||
459 | 0x2bff27ff, | ||
460 | 0x4e08fd1f, | ||
461 | 0xe5ff6e0f, | ||
462 | 0xaff87eef, | ||
463 | 0x7e0ffdef, | ||
464 | 0xf11f6079, | ||
465 | 0xabf8f542, | ||
466 | 0x7e0af11c, | ||
467 | 0x37cfae3a, | ||
468 | 0x7fec90be, | ||
469 | 0xadf8efdc, | ||
470 | 0xcfeae52f, | ||
471 | 0x7d0fe12b, | ||
472 | 0xf11c6079, | ||
473 | 0x7e0a4df8, | ||
474 | 0xcfea5dc4, | ||
475 | 0x7d0befec, | ||
476 | 0xcfea5dc6, | ||
477 | 0xe522efdc, | ||
478 | 0x5dc6cfda, | ||
479 | 0x4e08fd1f, | ||
480 | 0x6e0faff8, | ||
481 | 0x7c1f761f, | ||
482 | 0xfdeff91f, | ||
483 | 0x6079abf8, | ||
484 | 0x761cee24, | ||
485 | 0xf91f2bfb, | ||
486 | 0xefefcfec, | ||
487 | 0xf91f6079, | ||
488 | 0x761c27fb, | ||
489 | 0xefdf5da7, | ||
490 | 0xcfdc7fdd, | ||
491 | 0xd09c4bf8, | ||
492 | 0x47fd7c1f, | ||
493 | 0x761ccfcf, | ||
494 | 0x7eef7fed, | ||
495 | 0x7dfdf093, | ||
496 | 0xef7e7f1e, | ||
497 | 0x771efb18, | ||
498 | 0x6079e722, | ||
499 | 0xe6bbe5bb, | ||
500 | 0xae0ae5bb, | ||
501 | 0x600bae85, | ||
502 | 0xe2bbe2bb, | ||
503 | 0xe2bbe2bb, | ||
504 | 0xaf02e2bb, | ||
505 | 0xe2bb2ff9, | ||
506 | 0x6079e2bb | ||
507 | }; | ||
508 | |||
509 | uint patch_2f00[] = { | ||
510 | 0x30303030, | ||
511 | 0x3e3e3434, | ||
512 | 0xabbf9b99, | ||
513 | 0x4b4fbdbd, | ||
514 | 0x59949334, | ||
515 | 0x9fff37fb, | ||
516 | 0x9b177dd9, | ||
517 | 0x936956bb, | ||
518 | 0xfbdd697b, | ||
519 | 0xdd2fd113, | ||
520 | 0x1db9f7bb, | ||
521 | 0x36313963, | ||
522 | 0x79373369, | ||
523 | 0x3193137f, | ||
524 | 0x7331737a, | ||
525 | 0xf7bb9b99, | ||
526 | 0x9bb19795, | ||
527 | 0x77fdfd3d, | ||
528 | 0x573b773f, | ||
529 | 0x737933f7, | ||
530 | 0xb991d115, | ||
531 | 0x31699315, | ||
532 | 0x31531694, | ||
533 | 0xbf4fbdbd, | ||
534 | 0x35931497, | ||
535 | 0x35376956, | ||
536 | 0xbd697b9d, | ||
537 | 0x96931313, | ||
538 | 0x19797937, | ||
539 | 0x6935af78, | ||
540 | 0xb9b3baa3, | ||
541 | 0xb8788683, | ||
542 | 0x368f78f7, | ||
543 | 0x87778733, | ||
544 | 0x3ffffb3b, | ||
545 | 0x8e8f78b8, | ||
546 | 0x1d118e13, | ||
547 | 0xf3ff3f8b, | ||
548 | 0x6bd8e173, | ||
549 | 0xd1366856, | ||
550 | 0x68d1687b, | ||
551 | 0x3daf78b8, | ||
552 | 0x3a3a3f87, | ||
553 | 0x8f81378f, | ||
554 | 0xf876f887, | ||
555 | 0x77fd8778, | ||
556 | 0x737de8d6, | ||
557 | 0xbbf8bfff, | ||
558 | 0xd8df87f7, | ||
559 | 0xfd876f7b, | ||
560 | 0x8bfff8bd, | ||
561 | 0x8683387d, | ||
562 | 0xb873d87b, | ||
563 | 0x3b8fd7f8, | ||
564 | 0xf7338883, | ||
565 | 0xbb8ee1f8, | ||
566 | 0xef837377, | ||
567 | 0x3337b836, | ||
568 | 0x817d11f8, | ||
569 | 0x7378b878, | ||
570 | 0xd3368b7d, | ||
571 | 0xed731b7d, | ||
572 | 0x833731f3, | ||
573 | 0xf22f3f23 | ||
574 | }; | ||
575 | |||
576 | uint patch_2e00[] = { | ||
577 | 0x27eeeeee, | ||
578 | 0xeeeeeeee, | ||
579 | 0xeeeeeeee, | ||
580 | 0xeeeeeeee, | ||
581 | 0xee4bf4fb, | ||
582 | 0xdbd259bb, | ||
583 | 0x1979577f, | ||
584 | 0xdfd2d573, | ||
585 | 0xb773f737, | ||
586 | 0x4b4fbdbd, | ||
587 | 0x25b9b177, | ||
588 | 0xd2d17376, | ||
589 | 0x956bbfdd, | ||
590 | 0x697bdd2f, | ||
591 | 0xff9f79ff, | ||
592 | 0xff9ff22f | ||
593 | }; | ||
594 | #endif | ||
595 | |||
596 | /* | ||
597 | * USB SOF patch arrays. | ||
598 | */ | ||
599 | |||
600 | #ifdef CONFIG_USB_SOF_UCODE_PATCH | ||
601 | |||
602 | uint patch_2000[] = { | ||
603 | 0x7fff0000, | ||
604 | 0x7ffd0000, | ||
605 | 0x7ffb0000, | ||
606 | 0x49f7ba5b, | ||
607 | 0xba383ffb, | ||
608 | 0xf9b8b46d, | ||
609 | 0xe5ab4e07, | ||
610 | 0xaf77bffe, | ||
611 | 0x3f7bbf79, | ||
612 | 0xba5bba38, | ||
613 | 0xe7676076, | ||
614 | 0x60750000 | ||
615 | }; | ||
616 | |||
617 | uint patch_2f00[] = { | ||
618 | 0x3030304c, | ||
619 | 0xcab9e441, | ||
620 | 0xa1aaf220 | ||
621 | }; | ||
622 | #endif | ||
623 | |||
624 | void | ||
625 | cpm_load_patch(volatile immap_t *immr) | ||
626 | { | ||
627 | volatile uint *dp; /* Dual-ported RAM. */ | ||
628 | volatile cpm8xx_t *commproc; | ||
629 | volatile iic_t *iip; | ||
630 | volatile spi_t *spp; | ||
631 | volatile smc_uart_t *smp; | ||
632 | int i; | ||
633 | |||
634 | commproc = (cpm8xx_t *)&immr->im_cpm; | ||
635 | |||
636 | #ifdef CONFIG_USB_SOF_UCODE_PATCH | ||
637 | commproc->cp_rccr = 0; | ||
638 | |||
639 | dp = (uint *)(commproc->cp_dpmem); | ||
640 | for (i=0; i<(sizeof(patch_2000)/4); i++) | ||
641 | *dp++ = patch_2000[i]; | ||
642 | |||
643 | dp = (uint *)&(commproc->cp_dpmem[0x0f00]); | ||
644 | for (i=0; i<(sizeof(patch_2f00)/4); i++) | ||
645 | *dp++ = patch_2f00[i]; | ||
646 | |||
647 | commproc->cp_rccr = 0x0009; | ||
648 | |||
649 | printk("USB SOF microcode patch installed\n"); | ||
650 | #endif /* CONFIG_USB_SOF_UCODE_PATCH */ | ||
651 | |||
652 | #if defined(CONFIG_I2C_SPI_UCODE_PATCH) || \ | ||
653 | defined(CONFIG_I2C_SPI_SMC1_UCODE_PATCH) | ||
654 | |||
655 | commproc->cp_rccr = 0; | ||
656 | |||
657 | dp = (uint *)(commproc->cp_dpmem); | ||
658 | for (i=0; i<(sizeof(patch_2000)/4); i++) | ||
659 | *dp++ = patch_2000[i]; | ||
660 | |||
661 | dp = (uint *)&(commproc->cp_dpmem[0x0f00]); | ||
662 | for (i=0; i<(sizeof(patch_2f00)/4); i++) | ||
663 | *dp++ = patch_2f00[i]; | ||
664 | |||
665 | iip = (iic_t *)&commproc->cp_dparam[PROFF_IIC]; | ||
666 | # define RPBASE 0x0500 | ||
667 | iip->iic_rpbase = RPBASE; | ||
668 | |||
669 | /* Put SPI above the IIC, also 32-byte aligned. | ||
670 | */ | ||
671 | i = (RPBASE + sizeof(iic_t) + 31) & ~31; | ||
672 | spp = (spi_t *)&commproc->cp_dparam[PROFF_SPI]; | ||
673 | spp->spi_rpbase = i; | ||
674 | |||
675 | # if defined(CONFIG_I2C_SPI_UCODE_PATCH) | ||
676 | commproc->cp_cpmcr1 = 0x802a; | ||
677 | commproc->cp_cpmcr2 = 0x8028; | ||
678 | commproc->cp_cpmcr3 = 0x802e; | ||
679 | commproc->cp_cpmcr4 = 0x802c; | ||
680 | commproc->cp_rccr = 1; | ||
681 | |||
682 | printk("I2C/SPI microcode patch installed.\n"); | ||
683 | # endif /* CONFIG_I2C_SPI_UCODE_PATCH */ | ||
684 | |||
685 | # if defined(CONFIG_I2C_SPI_SMC1_UCODE_PATCH) | ||
686 | |||
687 | dp = (uint *)&(commproc->cp_dpmem[0x0e00]); | ||
688 | for (i=0; i<(sizeof(patch_2e00)/4); i++) | ||
689 | *dp++ = patch_2e00[i]; | ||
690 | |||
691 | commproc->cp_cpmcr1 = 0x8080; | ||
692 | commproc->cp_cpmcr2 = 0x808a; | ||
693 | commproc->cp_cpmcr3 = 0x8028; | ||
694 | commproc->cp_cpmcr4 = 0x802a; | ||
695 | commproc->cp_rccr = 3; | ||
696 | |||
697 | smp = (smc_uart_t *)&commproc->cp_dparam[PROFF_SMC1]; | ||
698 | smp->smc_rpbase = 0x1FC0; | ||
699 | |||
700 | printk("I2C/SPI/SMC1 microcode patch installed.\n"); | ||
701 | # endif /* CONFIG_I2C_SPI_SMC1_UCODE_PATCH) */ | ||
702 | |||
703 | #endif /* some variation of the I2C/SPI patch was selected */ | ||
704 | } | ||
705 | |||
706 | /* | ||
707 | * Take this entire routine out, since no one calls it and its | ||
708 | * logic is suspect. | ||
709 | */ | ||
710 | |||
711 | #if 0 | ||
712 | void | ||
713 | verify_patch(volatile immap_t *immr) | ||
714 | { | ||
715 | volatile uint *dp; | ||
716 | volatile cpm8xx_t *commproc; | ||
717 | int i; | ||
718 | |||
719 | commproc = (cpm8xx_t *)&immr->im_cpm; | ||
720 | |||
721 | printk("cp_rccr %x\n", commproc->cp_rccr); | ||
722 | commproc->cp_rccr = 0; | ||
723 | |||
724 | dp = (uint *)(commproc->cp_dpmem); | ||
725 | for (i=0; i<(sizeof(patch_2000)/4); i++) | ||
726 | if (*dp++ != patch_2000[i]) { | ||
727 | printk("patch_2000 bad at %d\n", i); | ||
728 | dp--; | ||
729 | printk("found 0x%X, wanted 0x%X\n", *dp, patch_2000[i]); | ||
730 | break; | ||
731 | } | ||
732 | |||
733 | dp = (uint *)&(commproc->cp_dpmem[0x0f00]); | ||
734 | for (i=0; i<(sizeof(patch_2f00)/4); i++) | ||
735 | if (*dp++ != patch_2f00[i]) { | ||
736 | printk("patch_2f00 bad at %d\n", i); | ||
737 | dp--; | ||
738 | printk("found 0x%X, wanted 0x%X\n", *dp, patch_2f00[i]); | ||
739 | break; | ||
740 | } | ||
741 | |||
742 | commproc->cp_rccr = 0x0009; | ||
743 | } | ||
744 | #endif | ||