Linux 2.6.25.3
[linux/fpc-iii.git] / arch / ppc / 8xx_io / commproc.c
blob9d656de0f0f19357d7f48724743bb6945ef7ba93
1 /*
2 * General Purpose functions for the global management of the
3 * Communication Processor Module.
4 * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
6 * In addition to the individual control of the communication
7 * channels, there are a few functions that globally affect the
8 * communication processor.
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.
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/cpm1.h>
38 #include <asm/io.h>
39 #include <asm/tlbflush.h>
40 #include <asm/rheap.h>
42 #define immr_map(member) \
43 ({ \
44 u32 offset = offsetof(immap_t, member); \
45 void *addr = ioremap (IMAP_ADDR + offset, \
46 sizeof( ((immap_t*)0)->member)); \
47 addr; \
50 #define immr_map_size(member, size) \
51 ({ \
52 u32 offset = offsetof(immap_t, member); \
53 void *addr = ioremap (IMAP_ADDR + offset, size); \
54 addr; \
57 static void m8xx_cpm_dpinit(void);
58 cpm8xx_t *cpmp; /* Pointer to comm processor space */
60 /* CPM interrupt vector functions.
62 struct cpm_action {
63 void (*handler)(void *);
64 void *dev_id;
66 static struct cpm_action cpm_vecs[CPMVEC_NR];
67 static irqreturn_t cpm_interrupt(int irq, void * dev);
68 static irqreturn_t cpm_error_interrupt(int irq, void *dev);
69 /* Define a table of names to identify CPM interrupt handlers in
70 * /proc/interrupts.
72 const char *cpm_int_name[] =
73 { "error", "PC4", "PC5", "SMC2",
74 "SMC1", "SPI", "PC6", "Timer 4",
75 "", "PC7", "PC8", "PC9",
76 "Timer 3", "", "PC10", "PC11",
77 "I2C", "RISC Timer", "Timer 2", "",
78 "IDMA2", "IDMA1", "SDMA error", "PC12",
79 "PC13", "Timer 1", "PC14", "SCC4",
80 "SCC3", "SCC2", "SCC1", "PC15"
83 static void
84 cpm_mask_irq(unsigned int irq)
86 int cpm_vec = irq - CPM_IRQ_OFFSET;
88 clrbits32(&((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr, (1 << cpm_vec));
91 static void
92 cpm_unmask_irq(unsigned int irq)
94 int cpm_vec = irq - CPM_IRQ_OFFSET;
96 setbits32(&((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr, (1 << cpm_vec));
99 static void
100 cpm_ack(unsigned int irq)
102 /* We do not need to do anything here. */
105 static void
106 cpm_eoi(unsigned int irq)
108 int cpm_vec = irq - CPM_IRQ_OFFSET;
110 out_be32(&((immap_t *)IMAP_ADDR)->im_cpic.cpic_cisr, (1 << cpm_vec));
113 struct hw_interrupt_type cpm_pic = {
114 .typename = " CPM ",
115 .enable = cpm_unmask_irq,
116 .disable = cpm_mask_irq,
117 .ack = cpm_ack,
118 .end = cpm_eoi,
121 void
122 m8xx_cpm_reset(void)
124 volatile immap_t *imp;
125 volatile cpm8xx_t *commproc;
127 imp = (immap_t *)IMAP_ADDR;
128 commproc = (cpm8xx_t *)&imp->im_cpm;
130 #ifdef CONFIG_UCODE_PATCH
131 /* Perform a reset.
133 commproc->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG);
135 /* Wait for it.
137 while (commproc->cp_cpcr & CPM_CR_FLG);
139 cpm_load_patch(imp);
140 #endif
142 /* Set SDMA Bus Request priority 5.
143 * On 860T, this also enables FEC priority 6. I am not sure
144 * this is what we really want for some applications, but the
145 * manual recommends it.
146 * Bit 25, FAM can also be set to use FEC aggressive mode (860T).
148 out_be32(&imp->im_siu_conf.sc_sdcr, 1),
150 /* Reclaim the DP memory for our use. */
151 m8xx_cpm_dpinit();
153 /* Tell everyone where the comm processor resides.
155 cpmp = (cpm8xx_t *)commproc;
158 /* This is called during init_IRQ. We used to do it above, but this
159 * was too early since init_IRQ was not yet called.
161 static struct irqaction cpm_error_irqaction = {
162 .handler = cpm_error_interrupt,
163 .mask = CPU_MASK_NONE,
165 static struct irqaction cpm_interrupt_irqaction = {
166 .handler = cpm_interrupt,
167 .mask = CPU_MASK_NONE,
168 .name = "CPM cascade",
171 void
172 cpm_interrupt_init(void)
174 int i;
176 /* Initialize the CPM interrupt controller.
178 out_be32(&((immap_t *)IMAP_ADDR)->im_cpic.cpic_cicr,
179 (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) |
180 ((CPM_INTERRUPT/2) << 13) | CICR_HP_MASK);
181 out_be32(&((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr, 0);
183 /* install the CPM interrupt controller routines for the CPM
184 * interrupt vectors
186 for ( i = CPM_IRQ_OFFSET ; i < CPM_IRQ_OFFSET + NR_CPM_INTS ; i++ )
187 irq_desc[i].chip = &cpm_pic;
189 /* Set our interrupt handler with the core CPU. */
190 if (setup_irq(CPM_INTERRUPT, &cpm_interrupt_irqaction))
191 panic("Could not allocate CPM IRQ!");
193 /* Install our own error handler. */
194 cpm_error_irqaction.name = cpm_int_name[CPMVEC_ERROR];
195 if (setup_irq(CPM_IRQ_OFFSET + CPMVEC_ERROR, &cpm_error_irqaction))
196 panic("Could not allocate CPM error IRQ!");
198 setbits32(&((immap_t *)IMAP_ADDR)->im_cpic.cpic_cicr, CICR_IEN);
202 * Get the CPM interrupt vector.
205 cpm_get_irq(void)
207 int cpm_vec;
209 /* Get the vector by setting the ACK bit and then reading
210 * the register.
212 out_be16(&((volatile immap_t *)IMAP_ADDR)->im_cpic.cpic_civr, 1);
213 cpm_vec = in_be16(&((volatile immap_t *)IMAP_ADDR)->im_cpic.cpic_civr);
214 cpm_vec >>= 11;
216 return cpm_vec;
219 /* CPM interrupt controller cascade interrupt.
221 static irqreturn_t
222 cpm_interrupt(int irq, void * dev)
224 /* This interrupt handler never actually gets called. It is
225 * installed only to unmask the CPM cascade interrupt in the SIU
226 * and to make the CPM cascade interrupt visible in /proc/interrupts.
228 return IRQ_HANDLED;
231 /* The CPM can generate the error interrupt when there is a race condition
232 * between generating and masking interrupts. All we have to do is ACK it
233 * and return. This is a no-op function so we don't need any special
234 * tests in the interrupt handler.
236 static irqreturn_t
237 cpm_error_interrupt(int irq, void *dev)
239 return IRQ_HANDLED;
242 /* A helper function to translate the handler prototype required by
243 * request_irq() to the handler prototype required by cpm_install_handler().
245 static irqreturn_t
246 cpm_handler_helper(int irq, void *dev_id)
248 int cpm_vec = irq - CPM_IRQ_OFFSET;
250 (*cpm_vecs[cpm_vec].handler)(dev_id);
252 return IRQ_HANDLED;
255 /* Install a CPM interrupt handler.
256 * This routine accepts a CPM interrupt vector in the range 0 to 31.
257 * This routine is retained for backward compatibility. Rather than using
258 * this routine to install a CPM interrupt handler, you can now use
259 * request_irq() with an IRQ in the range CPM_IRQ_OFFSET to
260 * CPM_IRQ_OFFSET + NR_CPM_INTS - 1 (16 to 47).
262 * Notice that the prototype of the interrupt handler function must be
263 * different depending on whether you install the handler with
264 * request_irq() or cpm_install_handler().
266 void
267 cpm_install_handler(int cpm_vec, void (*handler)(void *), void *dev_id)
269 int err;
271 /* If null handler, assume we are trying to free the IRQ.
273 if (!handler) {
274 free_irq(CPM_IRQ_OFFSET + cpm_vec, dev_id);
275 return;
278 if (cpm_vecs[cpm_vec].handler != 0)
279 printk(KERN_INFO "CPM interrupt %x replacing %x\n",
280 (uint)handler, (uint)cpm_vecs[cpm_vec].handler);
281 cpm_vecs[cpm_vec].handler = handler;
282 cpm_vecs[cpm_vec].dev_id = dev_id;
284 if ((err = request_irq(CPM_IRQ_OFFSET + cpm_vec, cpm_handler_helper,
285 0, cpm_int_name[cpm_vec], dev_id)))
286 printk(KERN_ERR "request_irq() returned %d for CPM vector %d\n",
287 err, cpm_vec);
290 /* Free a CPM interrupt handler.
291 * This routine accepts a CPM interrupt vector in the range 0 to 31.
292 * This routine is retained for backward compatibility.
294 void
295 cpm_free_handler(int cpm_vec)
297 request_irq(CPM_IRQ_OFFSET + cpm_vec, NULL, 0, 0,
298 cpm_vecs[cpm_vec].dev_id);
300 cpm_vecs[cpm_vec].handler = NULL;
301 cpm_vecs[cpm_vec].dev_id = NULL;
304 /* Set a baud rate generator. This needs lots of work. There are
305 * four BRGs, any of which can be wired to any channel.
306 * The internal baud rate clock is the system clock divided by 16.
307 * This assumes the baudrate is 16x oversampled by the uart.
309 #define BRG_INT_CLK (((bd_t *)__res)->bi_intfreq)
310 #define BRG_UART_CLK (BRG_INT_CLK/16)
311 #define BRG_UART_CLK_DIV16 (BRG_UART_CLK/16)
313 void
314 cpm_setbrg(uint brg, uint rate)
316 volatile uint *bp;
318 /* This is good enough to get SMCs running.....
320 bp = (uint *)&cpmp->cp_brgc1;
321 bp += brg;
322 /* The BRG has a 12-bit counter. For really slow baud rates (or
323 * really fast processors), we may have to further divide by 16.
325 if (((BRG_UART_CLK / rate) - 1) < 4096)
326 *bp = (((BRG_UART_CLK / rate) - 1) << 1) | CPM_BRG_EN;
327 else
328 *bp = (((BRG_UART_CLK_DIV16 / rate) - 1) << 1) |
329 CPM_BRG_EN | CPM_BRG_DIV16;
333 * dpalloc / dpfree bits.
335 static spinlock_t cpm_dpmem_lock;
337 * 16 blocks should be enough to satisfy all requests
338 * until the memory subsystem goes up...
340 static rh_block_t cpm_boot_dpmem_rh_block[16];
341 static rh_info_t cpm_dpmem_info;
343 #define CPM_DPMEM_ALIGNMENT 8
344 static u8* dpram_vbase;
345 static uint dpram_pbase;
347 void m8xx_cpm_dpinit(void)
349 spin_lock_init(&cpm_dpmem_lock);
351 dpram_vbase = immr_map_size(im_cpm.cp_dpmem, CPM_DATAONLY_BASE + CPM_DATAONLY_SIZE);
352 dpram_pbase = (uint)&((immap_t *)IMAP_ADDR)->im_cpm.cp_dpmem;
354 /* Initialize the info header */
355 rh_init(&cpm_dpmem_info, CPM_DPMEM_ALIGNMENT,
356 sizeof(cpm_boot_dpmem_rh_block) /
357 sizeof(cpm_boot_dpmem_rh_block[0]),
358 cpm_boot_dpmem_rh_block);
361 * Attach the usable dpmem area.
362 * XXX: This is actually crap. CPM_DATAONLY_BASE and
363 * CPM_DATAONLY_SIZE are a subset of the available dparm. It varies
364 * with the processor and the microcode patches applied / activated.
365 * But the following should be at least safe.
367 rh_attach_region(&cpm_dpmem_info, CPM_DATAONLY_BASE, CPM_DATAONLY_SIZE);
371 * Allocate the requested size worth of DP memory.
372 * This function returns an offset into the DPRAM area.
373 * Use cpm_dpram_addr() to get the virtual address of the area.
375 unsigned long cpm_dpalloc(uint size, uint align)
377 unsigned long start;
378 unsigned long flags;
380 spin_lock_irqsave(&cpm_dpmem_lock, flags);
381 cpm_dpmem_info.alignment = align;
382 start = rh_alloc(&cpm_dpmem_info, size, "commproc");
383 spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
385 return start;
387 EXPORT_SYMBOL(cpm_dpalloc);
389 int cpm_dpfree(unsigned long offset)
391 int ret;
392 unsigned long flags;
394 spin_lock_irqsave(&cpm_dpmem_lock, flags);
395 ret = rh_free(&cpm_dpmem_info, offset);
396 spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
398 return ret;
400 EXPORT_SYMBOL(cpm_dpfree);
402 unsigned long cpm_dpalloc_fixed(unsigned long offset, uint size, uint align)
404 unsigned long start;
405 unsigned long flags;
407 spin_lock_irqsave(&cpm_dpmem_lock, flags);
408 cpm_dpmem_info.alignment = align;
409 start = rh_alloc_fixed(&cpm_dpmem_info, offset, size, "commproc");
410 spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
412 return start;
414 EXPORT_SYMBOL(cpm_dpalloc_fixed);
416 void cpm_dpdump(void)
418 rh_dump(&cpm_dpmem_info);
420 EXPORT_SYMBOL(cpm_dpdump);
422 void *cpm_dpram_addr(unsigned long offset)
424 return (void *)(dpram_vbase + offset);
426 EXPORT_SYMBOL(cpm_dpram_addr);
428 uint cpm_dpram_phys(u8* addr)
430 return (dpram_pbase + (uint)(addr - dpram_vbase));
432 EXPORT_SYMBOL(cpm_dpram_phys);