blob: 752443df5ecf270ea67ca0e3c44dafd22d62a678 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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>
Jochen Friedrichb5677d82008-01-25 15:31:42 +010037#include <asm/cpm1.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#include <asm/io.h>
39#include <asm/tlbflush.h>
40#include <asm/rheap.h>
41
Vitaly Bordug7a6491d2007-04-10 21:52:04 +040042#define immr_map(member) \
43({ \
44 u32 offset = offsetof(immap_t, member); \
45 void *addr = ioremap (IMAP_ADDR + offset, \
Julia Lawall4217c7b2008-02-11 07:07:59 +110046 FIELD_SIZEOF(immap_t, member)); \
Vitaly Bordug7a6491d2007-04-10 21:52:04 +040047 addr; \
48})
49
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; \
55})
56
Linus Torvalds1da177e2005-04-16 15:20:36 -070057static void m8xx_cpm_dpinit(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -070058cpm8xx_t *cpmp; /* Pointer to comm processor space */
59
60/* CPM interrupt vector functions.
61*/
62struct cpm_action {
Al Viro39e3eb72006-10-09 12:48:42 +010063 void (*handler)(void *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070064 void *dev_id;
65};
66static struct cpm_action cpm_vecs[CPMVEC_NR];
Al Viro39e3eb72006-10-09 12:48:42 +010067static irqreturn_t cpm_interrupt(int irq, void * dev);
68static irqreturn_t cpm_error_interrupt(int irq, void *dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069/* Define a table of names to identify CPM interrupt handlers in
70 * /proc/interrupts.
71 */
72const 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"
81 };
82
83static void
84cpm_mask_irq(unsigned int irq)
85{
86 int cpm_vec = irq - CPM_IRQ_OFFSET;
87
Vitaly Bordug0ce928e2006-01-15 17:30:29 +030088 clrbits32(&((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr, (1 << cpm_vec));
Linus Torvalds1da177e2005-04-16 15:20:36 -070089}
90
91static void
92cpm_unmask_irq(unsigned int irq)
93{
94 int cpm_vec = irq - CPM_IRQ_OFFSET;
95
Vitaly Bordug0ce928e2006-01-15 17:30:29 +030096 setbits32(&((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr, (1 << cpm_vec));
Linus Torvalds1da177e2005-04-16 15:20:36 -070097}
98
99static void
100cpm_ack(unsigned int irq)
101{
102 /* We do not need to do anything here. */
103}
104
105static void
106cpm_eoi(unsigned int irq)
107{
108 int cpm_vec = irq - CPM_IRQ_OFFSET;
109
Marcelo Tosattie37b0c92005-10-28 17:46:10 -0700110 out_be32(&((immap_t *)IMAP_ADDR)->im_cpic.cpic_cisr, (1 << cpm_vec));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111}
112
113struct 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,
119};
120
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121void
Marcelo Tosatti079da352005-08-07 09:42:47 -0700122m8xx_cpm_reset(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123{
124 volatile immap_t *imp;
125 volatile cpm8xx_t *commproc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
127 imp = (immap_t *)IMAP_ADDR;
128 commproc = (cpm8xx_t *)&imp->im_cpm;
129
130#ifdef CONFIG_UCODE_PATCH
131 /* Perform a reset.
132 */
133 commproc->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG);
134
135 /* Wait for it.
136 */
137 while (commproc->cp_cpcr & CPM_CR_FLG);
138
139 cpm_load_patch(imp);
140#endif
141
142 /* Set SDMA Bus Request priority 5.
143 * On 860T, this also enables FEC priority 6. I am not sure
Simon Arlotta8de5ce2007-05-12 05:42:54 +1000144 * this is what we really want for some applications, but the
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 * manual recommends it.
146 * Bit 25, FAM can also be set to use FEC aggressive mode (860T).
147 */
Marcelo Tosattie37b0c92005-10-28 17:46:10 -0700148 out_be32(&imp->im_siu_conf.sc_sdcr, 1),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149
150 /* Reclaim the DP memory for our use. */
151 m8xx_cpm_dpinit();
152
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 /* Tell everyone where the comm processor resides.
154 */
155 cpmp = (cpm8xx_t *)commproc;
156}
157
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158/* 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.
160 */
161static struct irqaction cpm_error_irqaction = {
162 .handler = cpm_error_interrupt,
163 .mask = CPU_MASK_NONE,
164};
165static struct irqaction cpm_interrupt_irqaction = {
166 .handler = cpm_interrupt,
167 .mask = CPU_MASK_NONE,
168 .name = "CPM cascade",
169};
170
171void
172cpm_interrupt_init(void)
173{
174 int i;
175
176 /* Initialize the CPM interrupt controller.
177 */
Marcelo Tosattie37b0c92005-10-28 17:46:10 -0700178 out_be32(&((immap_t *)IMAP_ADDR)->im_cpic.cpic_cicr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) |
Marcelo Tosattie37b0c92005-10-28 17:46:10 -0700180 ((CPM_INTERRUPT/2) << 13) | CICR_HP_MASK);
181 out_be32(&((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182
183 /* install the CPM interrupt controller routines for the CPM
184 * interrupt vectors
185 */
186 for ( i = CPM_IRQ_OFFSET ; i < CPM_IRQ_OFFSET + NR_CPM_INTS ; i++ )
Ingo Molnard1bef4e2006-06-29 02:24:36 -0700187 irq_desc[i].chip = &cpm_pic;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188
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!");
192
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!");
197
Vitaly Bordug0ce928e2006-01-15 17:30:29 +0300198 setbits32(&((immap_t *)IMAP_ADDR)->im_cpic.cpic_cicr, CICR_IEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199}
200
201/*
202 * Get the CPM interrupt vector.
203 */
204int
Al Viro39e3eb72006-10-09 12:48:42 +0100205cpm_get_irq(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206{
207 int cpm_vec;
208
209 /* Get the vector by setting the ACK bit and then reading
210 * the register.
211 */
Marcelo Tosattie37b0c92005-10-28 17:46:10 -0700212 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 cpm_vec >>= 11;
215
216 return cpm_vec;
217}
218
219/* CPM interrupt controller cascade interrupt.
220*/
221static irqreturn_t
Al Viro39e3eb72006-10-09 12:48:42 +0100222cpm_interrupt(int irq, void * dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223{
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.
227 */
228 return IRQ_HANDLED;
229}
230
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.
235 */
236static irqreturn_t
Al Viro39e3eb72006-10-09 12:48:42 +0100237cpm_error_interrupt(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238{
239 return IRQ_HANDLED;
240}
241
242/* A helper function to translate the handler prototype required by
243 * request_irq() to the handler prototype required by cpm_install_handler().
244 */
245static irqreturn_t
Al Viro39e3eb72006-10-09 12:48:42 +0100246cpm_handler_helper(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247{
248 int cpm_vec = irq - CPM_IRQ_OFFSET;
249
Al Viro39e3eb72006-10-09 12:48:42 +0100250 (*cpm_vecs[cpm_vec].handler)(dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251
252 return IRQ_HANDLED;
253}
254
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).
261 *
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().
265 */
266void
Al Viro39e3eb72006-10-09 12:48:42 +0100267cpm_install_handler(int cpm_vec, void (*handler)(void *), void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268{
269 int err;
270
271 /* If null handler, assume we are trying to free the IRQ.
272 */
273 if (!handler) {
274 free_irq(CPM_IRQ_OFFSET + cpm_vec, dev_id);
275 return;
276 }
277
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;
283
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);
288}
289
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.
293 */
294void
295cpm_free_handler(int cpm_vec)
296{
297 request_irq(CPM_IRQ_OFFSET + cpm_vec, NULL, 0, 0,
298 cpm_vecs[cpm_vec].dev_id);
299
300 cpm_vecs[cpm_vec].handler = NULL;
301 cpm_vecs[cpm_vec].dev_id = NULL;
302}
303
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304/* 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.
308 */
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)
312
313void
314cpm_setbrg(uint brg, uint rate)
315{
316 volatile uint *bp;
317
318 /* This is good enough to get SMCs running.....
319 */
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.
324 */
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;
330}
331
332/*
333 * dpalloc / dpfree bits.
334 */
335static spinlock_t cpm_dpmem_lock;
336/*
337 * 16 blocks should be enough to satisfy all requests
338 * until the memory subsystem goes up...
339 */
340static rh_block_t cpm_boot_dpmem_rh_block[16];
341static rh_info_t cpm_dpmem_info;
342
343#define CPM_DPMEM_ALIGNMENT 8
Vitaly Bordug7a6491d2007-04-10 21:52:04 +0400344static u8* dpram_vbase;
345static uint dpram_pbase;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346
347void m8xx_cpm_dpinit(void)
348{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 spin_lock_init(&cpm_dpmem_lock);
350
Vitaly Bordug7a6491d2007-04-10 21:52:04 +0400351 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;
353
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 /* 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);
359
360 /*
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.
366 */
Timur Tabi4c356302007-05-08 14:46:36 -0500367 rh_attach_region(&cpm_dpmem_info, CPM_DATAONLY_BASE, CPM_DATAONLY_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368}
369
370/*
371 * Allocate the requested size worth of DP memory.
Marcelo Tosatti3d9e9dc2005-11-02 12:42:16 -0200372 * This function returns an offset into the DPRAM area.
373 * Use cpm_dpram_addr() to get the virtual address of the area.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 */
Timur Tabi4c356302007-05-08 14:46:36 -0500375unsigned long cpm_dpalloc(uint size, uint align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376{
Timur Tabi4c356302007-05-08 14:46:36 -0500377 unsigned long start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 unsigned long flags;
379
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);
384
Timur Tabi4c356302007-05-08 14:46:36 -0500385 return start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386}
387EXPORT_SYMBOL(cpm_dpalloc);
388
Timur Tabi4c356302007-05-08 14:46:36 -0500389int cpm_dpfree(unsigned long offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390{
391 int ret;
392 unsigned long flags;
393
394 spin_lock_irqsave(&cpm_dpmem_lock, flags);
Timur Tabi4c356302007-05-08 14:46:36 -0500395 ret = rh_free(&cpm_dpmem_info, offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
397
398 return ret;
399}
400EXPORT_SYMBOL(cpm_dpfree);
401
Timur Tabi4c356302007-05-08 14:46:36 -0500402unsigned long cpm_dpalloc_fixed(unsigned long offset, uint size, uint align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403{
Timur Tabi4c356302007-05-08 14:46:36 -0500404 unsigned long start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 unsigned long flags;
406
407 spin_lock_irqsave(&cpm_dpmem_lock, flags);
408 cpm_dpmem_info.alignment = align;
Timur Tabi4c356302007-05-08 14:46:36 -0500409 start = rh_alloc_fixed(&cpm_dpmem_info, offset, size, "commproc");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
411
Timur Tabi4c356302007-05-08 14:46:36 -0500412 return start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413}
414EXPORT_SYMBOL(cpm_dpalloc_fixed);
415
416void cpm_dpdump(void)
417{
418 rh_dump(&cpm_dpmem_info);
419}
420EXPORT_SYMBOL(cpm_dpdump);
421
Timur Tabi4c356302007-05-08 14:46:36 -0500422void *cpm_dpram_addr(unsigned long offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423{
Jochen Friedrichbc638182007-09-24 19:14:57 +0200424 return (void *)(dpram_vbase + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425}
426EXPORT_SYMBOL(cpm_dpram_addr);
Vitaly Bordug7a6491d2007-04-10 21:52:04 +0400427
428uint cpm_dpram_phys(u8* addr)
429{
430 return (dpram_pbase + (uint)(addr - dpram_vbase));
431}
432EXPORT_SYMBOL(cpm_dpram_phys);