aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ppc/8xx_io/commproc.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/ppc/8xx_io/commproc.c
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/commproc.c')
-rw-r--r--arch/ppc/8xx_io/commproc.c464
1 files changed, 464 insertions, 0 deletions
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
42extern int get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep);
43
44static void m8xx_cpm_dpinit(void);
45static uint host_buffer; /* One page of host buffer */
46static uint host_end; /* end + 1 */
47cpm8xx_t *cpmp; /* Pointer to comm processor space */
48
49/* CPM interrupt vector functions.
50*/
51struct cpm_action {
52 void (*handler)(void *, struct pt_regs * regs);
53 void *dev_id;
54};
55static struct cpm_action cpm_vecs[CPMVEC_NR];
56static irqreturn_t cpm_interrupt(int irq, void * dev, struct pt_regs * regs);
57static irqreturn_t cpm_error_interrupt(int irq, void *dev, struct pt_regs * regs);
58static void alloc_host_memory(void);
59/* Define a table of names to identify CPM interrupt handlers in
60 * /proc/interrupts.
61 */
62const 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
73static void
74cpm_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
81static void
82cpm_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
89static void
90cpm_ack(unsigned int irq)
91{
92 /* We do not need to do anything here. */
93}
94
95static void
96cpm_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
103struct 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
111extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr);
112
113void
114m8xx_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 */
165static void
166alloc_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 */
180static struct irqaction cpm_error_irqaction = {
181 .handler = cpm_error_interrupt,
182 .mask = CPU_MASK_NONE,
183};
184static struct irqaction cpm_interrupt_irqaction = {
185 .handler = cpm_interrupt,
186 .mask = CPU_MASK_NONE,
187 .name = "CPM cascade",
188};
189
190void
191cpm_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 */
223int
224cpm_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*/
240static irqreturn_t
241cpm_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 */
255static irqreturn_t
256cpm_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 */
264static irqreturn_t
265cpm_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 */
285void
286cpm_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 */
314void
315cpm_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 */
327uint
328m8xx_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
353void
354cpm_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 */
375static spinlock_t cpm_dpmem_lock;
376/*
377 * 16 blocks should be enough to satisfy all requests
378 * until the memory subsystem goes up...
379 */
380static rh_block_t cpm_boot_dpmem_rh_block[16];
381static rh_info_t cpm_dpmem_info;
382
383#define CPM_DPMEM_ALIGNMENT 8
384
385void 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 */
413uint 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}
425EXPORT_SYMBOL(cpm_dpalloc);
426
427int 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}
438EXPORT_SYMBOL(cpm_dpfree);
439
440uint 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}
452EXPORT_SYMBOL(cpm_dpalloc_fixed);
453
454void cpm_dpdump(void)
455{
456 rh_dump(&cpm_dpmem_info);
457}
458EXPORT_SYMBOL(cpm_dpdump);
459
460void *cpm_dpram_addr(uint offset)
461{
462 return ((immap_t *)IMAP_ADDR)->im_cpm.cp_dpmem + offset;
463}
464EXPORT_SYMBOL(cpm_dpram_addr);